```r
#install.packages(\knitr\)
#install.packages(\grid\)
#install.packages(\MLRMPA\)
#install.packages(\dprep\)
#install.packages(\normalr\)
#install.packages(\ggcorrplot\)
#install.packages(\RColorBrewer\)
#install.packages(\rgdal\)
#install.packages(\jsonlite\)
#install.packages(\RColorBrewer\)
#install.packages(\readr\)
Sys.setlocale(\LC_ALL\, \English\)
Sys.setenv(\LANGUAGE\=\En\)

<!-- rnb-source-end -->

<!-- rnb-chunk-end -->


<!-- rnb-text-begin -->



<!-- rnb-text-end -->


<!-- rnb-chunk-begin -->


<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxubGlicmFyeShncmlkRXh0cmEpXG5gYGAifQ== -->

```r
library(gridExtra)
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
3: Unknown or uninitialised column: `Country`. 
4: Unknown or uninitialised column: `Country`. 
5: Unknown or uninitialised column: `Country`. 
library(dplyr)
library(lubridate)
library(magrittr)
library(ggplot2)
library(tidyr)
library(knitr)
library(normalr)
library(ggcorrplot)
library(leaflet)
library(plotly)
library(RColorBrewer)
library(readr)

#??src_mysql
my_db <- src_mysql(
  dbname = "covid",
  host = "127.0.0.1",
  user = "root",
  password = "1234"
)
my_db
src:  mysql 8.0.21 [root@127.0.0.1:/covid]
tbls: covid19_confirmed, covid19_deaths, covid19_recovered, covid19_thailand, covidus, data_ethnic,
  data_gender, data_health, data_lockdown, data_population, data_testing, gdp19, healthranking, population,
  pornhub, sars_2003, us_gdp, us_homeless, world_temp
##import data
df_conf <- tbl(my_db, sql("select * from covid19_confirmed"))
df_conf <- as.data.frame(df_conf)
df_conf
df_deaths <- tbl(my_db, sql("select * from covid19_deaths"))
df_deaths <- as.data.frame(df_deaths)
df_deaths
df_recover <- tbl(my_db, sql("select * from covid19_recovered"))
df_recover <- as.data.frame(df_recover)
df_recover
##check the time frame of the data
n.col <- ncol(df_conf)
dates <- names(df_conf)[5:n.col]%>% mdy()
range(dates)
[1] "2020-01-22" "2021-01-22"
min.date <- min(dates)
max.date <- max(dates)
min.date.txt <- min.date %>% format('%d %b %Y')
max.date.txt <- max.date %>% format('%d %b %Y')
#clean data
cleanData <- function(data) {
  ## remove some columns
  data %<>% select(-c(Province.State, Lat, Long)) %>% rename(country=Country.Region)
  ## convert from wide to long format
  data %<>% gather(key=date, value=count, -country)
  ## convert from character to date
  data %<>% mutate(date = date %>% mdy())
  ## aggregate by country
  data %<>% group_by(country, date) %>% summarise(count=sum(count, na.rm=T)) %>% as.data.frame()
  return(data)
}
## clean the three data sets
data.confirmed <- df_conf %>% cleanData() %>% rename(confirmed=count)
`summarise()` regrouping output by 'country' (override with `.groups` argument)
data.deaths <- df_deaths %>% cleanData() %>% rename(deaths=count)
`summarise()` regrouping output by 'country' (override with `.groups` argument)
data.recovered <- df_recover %>% cleanData() %>% rename(recovered=count)
`summarise()` regrouping output by 'country' (override with `.groups` argument)
data <- data.confirmed %>% merge(data.deaths, all=T) %>% merge(data.recovered, all=T)
data
## countries/regions with confirmed cases, excl. cruise ships
countries <- data %>% pull(country) %>% setdiff('Cruise Ship')
data
data.world <- data %>% group_by(date) %>%
Warning message:
Unknown or uninitialised column: `Country`. 
  summarise(country='World',
            confirmed = sum(confirmed, na.rm=T),
            deaths = sum(deaths, na.rm=T),
            recovered = sum(recovered, na.rm=T))
`summarise()` ungrouping output (override with `.groups` argument)
data %<>% rbind(data.world)
data
data %<>% mutate(current.confirmed = confirmed - deaths - recovered)
View(data)
# World confirmed cases map
There were 11 warnings (use warnings() to see them)
world.location <- df_conf
View(world.location )
world.location$confirmed <- world.location [, ncol(world.location )]
world.location  %<>% select(c(Country.Region, Province.State, Lat, Long, confirmed)) %>%
  mutate(txt=paste0(Country.Region, ' - ', Province.State, ': ', confirmed))
map.confirmed <- leaflet(width=1200, height=800) %>% addTiles()
# circle marker (units in pixels)
map.confirmed %<>% addCircleMarkers(x$Long, x$Lat,
                        # radius=2+log2(x$confirmed),
                        radius=0.02*sqrt(x$confirmed),
                        stroke=F,
                        color='red', fillOpacity=0.3,
                        label=x$txt)
# world
map.confirmed
## China
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
map.confirmed %>% setView(95, 35, zoom=4)

## Australia and New Zealand
map.confirmed %>% setView(135, -27, zoom=4)

## US and Canada
map.confirmed %>% setView(-105, 40, zoom=4)

## Europe
map.confirmed %>% setView(10, 50, zoom=4)
# new.confirmed new.deaths new.recovered
Warning message:
Unknown or uninitialised column: `Country`. 
data %<>% arrange(country, date)
n <- nrow(data)
day1 <- min(data$date)
data %<>% mutate(new.confirmed = ifelse(date == day1, NA, confirmed - lag(confirmed, n=1)),
                 new.deaths = ifelse(date == day1, NA, deaths - lag(deaths, n=1)),
                 new.recovered = ifelse(date == day1, NA, recovered - lag(recovered, n=1)))
data %<>% mutate(new.confirmed = ifelse(new.confirmed < 0, 0, new.confirmed),
                 new.deaths = ifelse(new.deaths < 0, 0, new.deaths),
                 new.recovered = ifelse(new.recovered < 0, 0, new.recovered))
## death rate based on total deaths and recovered cases
data %<>% mutate(rate.upper = (100 * deaths / (deaths + recovered)) %>% round(1),
                 rate.upper = ifelse(is.nan(rate.upper), 0, rate.upper))

## lower bound: death rate based on total confirmed cases
data %<>% mutate(rate.lower = (100 * deaths / confirmed) %>% round(1),
                 rate.lower = ifelse(is.nan(rate.lower), 0, rate.lower))

## death rate based on the number of death/recovered on every single day
data %<>% mutate(rate.daily = (100 * new.deaths / (new.deaths + new.recovered)) %>% round(1),
                 rate.daily = ifelse(is.nan(rate.daily), 0, rate.daily))

View(data)
## convert from wide to long format
data.long <- data %>%
  select(c(country, date, confirmed, current.confirmed, recovered, deaths)) %>%
  gather(key=type, value=count, -c(country, date))
## set factor levels to show them in a desirable order
data.long %<>% mutate(type=recode_factor(type, confirmed='Total Confirmed',
                                         current.confirmed='Current Confirmed',
                                         recovered='Recovered',
                                         deaths='Deaths'))
View(data.long)
##Number of case World
There were 21 warnings (use warnings() to see them)
world <- filter(data.long,country == 'World')
world
plot_world.area <- world %>% filter(type != 'Total Confirmed') %>%
  ggplot(aes(x=date, y=count)) +
  geom_area(aes(fill=type), alpha=0.5) +
  labs(title=paste0('Numbers of Cases Worldwide - ', max.date.txt)) +
  scale_fill_manual(values=c('red', 'green', 'black')) +
  theme(legend.title=element_blank(), legend.position='bottom',
        plot.title = element_text(size=7),
        axis.title.x=element_blank(),
        axis.title.y=element_blank(),
        legend.key.size=unit(0.2, 'cm'),
        legend.text=element_text(size=6),
        axis.text=element_text(size=7),
        axis.text.x=element_text(angle=45, hjust=1))
plot_world.line <- world %>%
  ggplot(aes(x=date, y=count)) +
  geom_line(aes(color=type)) +
  labs(title=paste0('Numbers of Cases Worldwide (log scale) - ', max.date.txt)) +
  scale_color_manual(values=c('purple', 'red', 'green', 'black')) +
  theme(legend.title=element_blank(), legend.position='bottom',
        plot.title = element_text(size=7),
        axis.title.x=element_blank(),
        axis.title.y=element_blank(),
        legend.key.size=unit(0.2, 'cm'),
        legend.text=element_text(size=6),
        axis.text=element_text(size=7),
        axis.text.x=element_text(angle=45, hjust=1)) +
  scale_y_continuous(trans='log10')
## show two plots side by side
grid.arrange(plot1, plot2, ncol=2)


gly.plot_world.area <- ggplotly(plot_world.area)
gly.plot_world.area


gly.plot_world.line <- ggplotly(plot_world.line)
gly.plot_world.line
data.world.all <- data %>% filter(country == "World")
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
3: Unknown or uninitialised column: `Country`. 
4: Unknown or uninitialised column: `Country`. 
5: Unknown or uninitialised column: `Country`. 
## a scatter plot with a smoothed line and vertical x-axis labels
plot_acc.deaths <- ggplot(data.world.all, aes(x=date, y=deaths)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='Accumulative Deaths') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
plot_recovered <- ggplot(data.world.all, aes(x=date, y=recovered)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='Accumulative Recovered Cases') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
plot_new.deaths <- ggplot(data.world.all, aes(x=date, y=new.deaths)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='New Deaths') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
plot_new.recovered <- ggplot(data.world.all, aes(x=date, y=new.recovered)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='New Recovered Cases') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
## show four plots together, with 2 plots in each row
grid.arrange(plot_acc.deaths, plot_recovered, plot_new.deaths, plot_new.recovered, nrow=2)

## convert from wide to long format, for drawing area plots
rates.long <- data %>%
  select(c(country, date, rate.upper, rate.lower, rate.daily)) %>%
  gather(key=type, value=count, -c(country, date))
# set factor levels to show them in a desirable order
rates.long %<>% mutate(type=recode_factor(type, rate.daily='Daily',
                             rate.upper='Upper bound',
                             rate.lower = 'Lower bound')) 
#View(rates.long) 

g <- rates.long %>% filter(country == "World") %>% 
  ggplot(aes(x = date, y = count, color = type)) + 
  geom_line() +
  labs(title = "World Death Rate (%)") +
  xlab("") + ylab("Death Rate (%)")

g


gly.death <- ggplotly(g)
gly.death
## ranking by confirmed cases
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
3: Unknown or uninitialised column: `Country`. 
4: Unknown or uninitialised column: `Country`. 
5: Unknown or uninitialised column: `Country`. 
data.latest.all <- data %>% filter(date == max(date)) %>%
  select(country, date,confirmed, new.confirmed, current.confirmed,
         recovered, deaths, new.deaths, death.rate=rate.lower) %>%
  mutate(ranking = dense_rank(desc(confirmed))) %>%
  arrange(ranking)
View(data.latest.all)

k <- 20
## top 20 countries: 21 incl. 'World'
top.countries <- data.latest.all %>% filter(ranking <= k + 1) %>%
  arrange(ranking) %>% pull(country) %>% as.character()
top.countries %>% setdiff('World') %>% print()
 [1] "US"             "India"          "Brazil"         "Russia"         "United Kingdom" "France"        
 [7] "Spain"          "Italy"          "Turkey"         "Germany"        "Colombia"       "Argentina"     
[13] "Mexico"         "Poland"         "South Africa"   "Iran"           "Ukraine"        "Peru"          
[19] "Indonesia"      "Netherlands"   
data.latest <- data.latest.all %>% filter(!is.na(country)) %>%
Warning message:
Unknown or uninitialised column: `Country`. 
  mutate(country=ifelse(ranking <= k + 1, as.character(country), 'Others')) %>%
  mutate(country=country %>% factor(levels=c(top.countries, 'Others')))
data.latest %<>% group_by(country) %>%
  summarise(confirmed=sum(confirmed), new.confirmed=sum(new.confirmed),
            current.confirmed=sum(current.confirmed),
            recovered=sum(recovered), deaths=sum(deaths), new.deaths=sum(new.deaths)) %>%
  mutate(death.rate=(100 * deaths/confirmed) %>% round(1)) 
`summarise()` ungrouping output (override with `.groups` argument)
data.latest
data.latest %<>% select(c(country, confirmed, deaths, death.rate,
                          new.confirmed, new.deaths, current.confirmed,recovered)) %>%
  mutate(recover.rate=(100 * recovered/confirmed) %>% round(1))
data.latest

df_pop <- tbl(my_db, sql("select * from population "))
df_pop <- as.data.frame(df_pop)
df_pop <- rename(df_pop,"country"="Country")

# Add World Population
world_pop <- sum(df_pop$`Population (2020)`)
df_pop[nrow(df_pop) + 1,] = c("World", world_pop)

# Add Other Countries Population
top_pop <- filter(df_pop, df_pop$country %in% top.countries & df_pop$country != "World")
top_pop <- sum(top_pop$`Population (2020)` %>% as.numeric())
others_pop <- (world_pop - top_pop) 
df_pop[nrow(df_pop) + 1,] = c("Others", others_pop)
View(df_pop)

data.latest <- merge(x = data.latest, y = df_pop, by = "country", all.x = TRUE) 
data.latest
data.latest <- rename(data.latest,"population" = "Population (2020)")
data.latest$population <- data.latest$population %>% as.numeric()
data.latest  <- data.latest %>%
  select(c(country, confirmed, deaths, death.rate,
                          new.confirmed, new.deaths,
                          current.confirmed, recovered, recover.rate, population)) %>%
  mutate(confirm.rate = (100 * confirmed / population) %>% round(1))
data.latest
## % of death
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
3: Unknown or uninitialised column: `Country`. 
data.latest %>% mutate(death.rate=death.rate %>% format(nsmall=1) %>% paste0('%'))

## convert from wide to long format, for drawing area plots
data.latest.long <- data.latest %>% filter(country!='World') %>%
  gather(key=type, value=count, -country)
## set factor levels to show them with proper text and in a desirable order
data.latest.long %<>% mutate(type=recode_factor(type,
                                                confirmed='Total Confirmed',
                                                deaths='Total Deaths',
                                                death.rate='Death Rate (%)',
                                                new.confirmed='New Confirmed (compared with one day before)',
                                                new.deaths='New Deaths (compared with one day before)',
                                                current.confirmed='Current Confirmed',
                                                recover.rate = 'Recovered Rate(%)',
                                                confirm.rate = 'Confirmed Rate(%)'))
#View(data.latest.long)
data.one.dem <- filter(data.latest.long,type=='Total Confirmed'
                       | type=='Total Deaths'
                       | type=='Current Confirmed')
data.two.dem <- filter(data.latest.long,type=='Death Rate (%)'
                     #  | type=='New Confirmed (compared with one day before)'
                    #   | type=='New Deaths (compared with one day before)'
                       | type=='Recovered Rate(%)'
                       | type=='Confirmed Rate(%)')
data.two.dem
## bar chart
data.one.dem %>% ggplot(aes(x=country, y=count, fill=country, group=country)) +
  geom_bar(stat='identity') +
  geom_text(aes(label=count, y=count), size=3, vjust=0) +
  xlab('') + ylab('') +
  labs(title=paste0('Top 20 Countries with Most Confirmed Cases - ', max.date.txt)) +
  scale_fill_discrete(name='Country', labels=aes(count)) +
  theme(legend.title=element_blank(),
        legend.position='none',
        plot.title=element_text(size=13),
        axis.text=element_text(size=8),
        axis.text.x=element_text(angle=45, hjust=1)) +
  facet_wrap(~type, ncol=1, scales='free_y')

data.two.dem$facet <- factor(data.two.dem$type, levels = c('Confirmed Rate(%)', 'Recovered Rate(%)','Death Rate (%)'))
data.two.dem %>% 
  ggplot(aes(x=country, y=count, fill=country, group=country)) +
  geom_bar(stat='identity') +
  geom_text(aes(label=count, y=count), size=4, vjust=0) +
  xlab('') + ylab('') +
  labs(title=paste0('Top 20 Countries with Most Confirmed Cases - ', max.date.txt)) +
  scale_fill_discrete(name='Country', labels=aes(count)) +
  theme(legend.title=element_blank(),
        legend.position='none',
        plot.title=element_text(size=13),
        axis.text=element_text(size=10),
        axis.text.x=element_text(size=10,angle=45, hjust=1)) +
  facet_wrap(~facet, ncol=1, scales='free_y')

##GDP
df_gdp2019 <- tbl(my_db, sql("select * from gdp19"))
df_gdp2019 <- as.data.frame(df_gdp2019)
df_gdp2019
#healthranking
df_healt <- tbl(my_db, sql("select * from healthranking"))
df_healt <- as.data.frame(df_healt)
df_healt <- select(df_healt,c("country","healthCareIndex"))
View(df_healt)
#Top20Pornhub
Warning message:
Unknown or uninitialised column: `Country`. 
df_pornhub <- tbl(my_db, sql("select * from Pornhub"))
df_pornhub <- as.data.frame(df_pornhub)
df_pornhub
# temp data
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
df_temp <- tbl(my_db, sql("select * from world_temp"))
df_temp <- as.data.frame(df_temp) 
df_temp$Country[df_temp$Country == "United States"] <- "US"

df_city <- select(df_temp,c("Country","City")) %>%
  rename(country=Country) %>% 
  rename(city=City)

numofcity <- aggregate(city ~ country, data = df_city, length)

#clean data
df_temp <- select(df_temp,c("Country","Avg_Year")) %>%
  rename(country=Country)
View(df_temp)

#df_temp <- data.frame(country=df_temp[,1],avg=rowMeans(df_temp[,-1]))
df_temp <- df_temp %<>% group_by(country) %>% summarise(avg_temp = mean(Avg_Year,na.rm = TRUE)%>% round(1))
`summarise()` ungrouping output (override with `.groups` argument)
df_temp
##temp bar
Warning message:
Unknown or uninitialised column: `Country`. 
df_temp.all <- df_temp %>% merge(data.latest.all)
View(df_temp.all)
df_temp_top.all <- df_temp.all %>% filter(country %in% top.countries) %>%
  mutate(ranking = ranking - 1) %>%
  arrange(ranking)
View(df_temp_top.all)
g_temp_top <- df_temp_top.all %>%
  ggplot(aes(x = reorder(country, ranking), y = avg_temp, fill = avg_temp)) +
  labs(title=paste0("Temperature in Top  20 countries"), subtitle = "Average Temperature in Top 20 countries with most confirmed cases (°C) (2020)") +
  scale_color_gradient(low = "#93DBFF", high = "#FF7771") +
  geom_text(aes(label=avg_temp, y=avg_temp), size=4, vjust=-0.5) +
  geom_bar(stat = "identity", position = "dodge") +
  theme(
        legend.title=element_blank(),
        legend.position='none',
        plot.title=element_text(size = 15, hjust = 0.5),
        plot.subtitle = element_text(size = 12, hjust = 0.5),
        axis.text=element_text(size=8),
        axis.text.x=element_text(size = 9, angle=45, hjust=1)) +
  scale_x_discrete(name = "Country") +
  scale_y_discrete(name = "Average Temperature")

           #labs(title = "Temperature in Top  20 countries", subtitle = "Temperature in Top 20 countries with most confirmed cases (°C)")
         
#g_temp_top         
g_temp_top 

#data.latest.all
lat.long <- rename(df_conf, "country" = "Country.Region", "city" = "Province.State") %>% 
  select("country", "Lat", "Long") %>% 
  merge(df_temp.all[c("country","confirmed", "recovered", "deaths", "avg_temp", "ranking")], by = "country") %>%
  distinct(country, .keep_all = TRUE) %>%
  mutate(ranking = ranking - 1) %>%
  arrange(ranking)
View(lat.long)
label_world <- lat.long 
Warning message:
Unknown or uninitialised column: `Country`. 
label_world$avg_temp <- as.numeric(label_world[, names(label_world) %in% c("avg_temp")])
label_world <- label_world %>%  
  mutate(txt=paste0('<b>',ranking, '</b>',
                    '<br/>','<b>',country, '</b>',
                    '<br/>', "Temperature:  ",avg_temp, ' °C',
                    '<br/>', "Confirmed:  ", confirmed, 
                    '<br/>', "Deaths: ", deaths,
                    '<br/>', "Recovered: ", recovered
                    )) 

label_world$txt <- label_world$txt %>% lapply(htmltools::HTML)
label_world 

label_top <- label_world %>% filter(ranking < 21)
label_top
# Temperature Map
wpal <- colorNumeric("YlOrRd", label_world$avg_temp, n = 4)

topIcon <- makeIcon("star.png",
  #iconUrl = "https://static.vecteezy.com/system/resources/previews/001/189/063/non_2x/star-rounded-png.png",
  iconWidth = 10, iconHeight = 10
  #iconAnchorX = 20, iconAnchorY = 20
  
)

label_world <- label_world %>% filter(ranking > 20) 
  
m <- leaflet(width=1200, height=800) %>% addTiles()  
m %<>%  addCircleMarkers(label_world$Long, label_world$Lat,
                        # radius=2+log2(x$confirmed),
                        radius=10,#*log2(m.world$avg.temp),
                        stroke=F,
                        #color='red',
                        color = wpal(label_world$avg_temp), 
                        fillOpacity=0.5,
                        #popup=label.top$txt
                        label= label_world$txt,
                        group = "World"
                        ) %>%
  
  addCircleMarkers(label_top$Long, label_top$Lat,
                        # radius=2+log2(x$confirmed),
                        radius=10,#*log2(m.world$avg.temp),
                        stroke=F,
                        #color='red',
                        color = wpal(label_top$avg_temp), 
                        fillOpacity=0.5,
                        #popup=label.top$txt
                        label= label_top$txt,
                        group = "Top 20 Countries"
                        ) %>%
  
  addLabelOnlyMarkers(label_top$Long, label_top$Lat, label = label_top$ranking,
                      labelOptions = labelOptions(noHide = TRUE, textOnly = TRUE, 
                                                  direction = "head", 
                                                  offset = c(5,4)),
                      group = "Top 20 Countries") %>%
  
  addLegend("bottomright", pal = wpal, values = label_world$avg_temp, opacity = 1,
            labFormat = labelFormat(suffix = " °C"),
            title = "Temperature") %>% 
  
  addLayersControl(
    #baseGroups = c("OSM (default)", "Toner", "Toner Lite"),
    overlayGroups = c("Top 20 Countries", "World"),
    options = layersControlOptions(collapsed = FALSE)
  )
m
gdp.top20 <- df_gdp2019 %>%
Warning message:
Unknown or uninitialised column: `Country`. 
  select(c("rank", "country", "GDP (millions of US dollars)")) %>%
  merge(data.latest.all %>% 
          select(country, ranking, confirmed, recovered, deaths) %>% 
          filter(country %in% top.countries & country != "World"), by = "country") %>%
  arrange(ranking) %>% 
  mutate(ranking = ranking - 1) 
gdp.top20 %<>% rename("GDP" = "GDP (millions of US dollars)")
gdp.top20

g <- ggplot(gdp.top20, aes(x = GDP, y = reorder(country, -ranking))) +
  geom_histogram(stat = "identity", aes(fill = GDP))+ 
  scale_fill_gradient("GDP", low = "#FF4038", high = "#50E952") + 
  labs(title=paste0("GDP  of Top 20 Countries in 2019 (millions of US dollars)")) +
  geom_text(aes(label=GDP, x = GDP), size=3.5, hjust=-0.2) +
  xlab("GDP (millions of US dollars)") +
  ylab("") +
  theme(legend.title=element_blank())
Ignoring unknown parameters: binwidth, bins, pad
g


gly.top.gdp <- ggplotly(g)
gly.top.gdp
# Pornhub
Warning message:
Unknown or uninitialised column: `Country`. 
df_pornhub <- tbl(my_db, sql("select * from pornhub"))
df_pornhub <- as.data.frame(df_pornhub)
df_pornhub
# Sars
df_sars <- tbl(my_db, sql("select * from sars_2003"))
df_sars <- as.data.frame(df_sars)
#View(df_sars)

dates.s <- df_sars[,1]%>% mdy()
range(dates.s)
[1] "2003-03-17" "2003-07-11"
min.date.s <- min(dates.s)
max.date.s <- max(dates.s)
min.date.txt.s <- min.date.s %>% format('%d %b %Y')
max.date.txt.s <- max.date.s %>% format('%d %b %Y')
day1.sars <- min.date.s
# clean data Sars 
data.sars <- df_sars %>% rename(c("date" = "Date", "confirmed" = "Cumulative_number"  ,"deaths" = "Number_deaths", "recovered" = "Number_recovered")) %>%
  mutate(date = date %>% mdy()) %>%
  group_by(country, date) %>% as.data.frame() 
View(data.sars)


# Add World's Sars cases 
world.sars <- data.sars %>% group_by(date) %>% 
  summarise(country='World',
            confirmed = sum(confirmed, na.rm=T),
            deaths = sum(deaths, na.rm=T),
            recovered = sum(recovered, na.rm=T))
`summarise()` ungrouping output (override with `.groups` argument)
data.sars %<>% rbind(world.sars)
data.sars %<>% mutate(current.confirmed = confirmed - deaths - recovered)
#View(world.sars) 
#View(data.sars)
#rate
Warning message:
Unknown or uninitialised column: `Country`. 
data.sars %<>% arrange(country, date)
n <- nrow(data.sars)
day1.sars <- min(data.sars$date)
data.sars %<>% mutate(new.confirmed = ifelse(date == day1.sars, NA, confirmed - lag(confirmed, n=1)),
                 new.deaths = ifelse(date == day1.sars, NA, deaths - lag(deaths, n=1)),
                 new.recovered = ifelse(date == day1.sars, NA, recovered - lag(recovered, n=1)))
data.sars %<>% mutate(new.confirmed = ifelse(new.confirmed < 0, 0, new.confirmed),
                 new.deaths = ifelse(new.deaths < 0, 0, new.deaths),
                 new.recovered = ifelse(new.recovered < 0, 0, new.recovered))
## death rate based on total deaths and recovered cases
data.sars %<>% mutate(rate.upper = (100 * deaths / (deaths + recovered)) %>% round(1),
                 rate.upper = ifelse(is.nan(rate.upper), 0, rate.upper))

## lower bound: death rate based on total confirmed cases
data.sars %<>% mutate(rate.lower = (100 * deaths / confirmed) %>% round(1),
                 rate.lower = ifelse(is.nan(rate.lower), 0, rate.lower))

## death rate based on the number of death/recovered on every single day
data.sars %<>% mutate(rate.daily = (100 * new.deaths / (new.deaths + new.recovered)) %>% round(1),
                 rate.daily = ifelse(is.nan(rate.daily), 0, rate.daily))

View(data.sars)
## convert from wide to long format
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
data.sars.long <- data.sars %>%
  select(c(country, date, confirmed, current.confirmed, recovered, deaths)) %>%
  gather(key=type, value=count, -c(country, date))
## set factor levels to show them in a desirable order
data.sars.long %<>% mutate(type=recode_factor(type, confirmed='Total Confirmed',
                                         current.confirmed='Current Confirmed',
                                         recovered='Recovered',
                                         deaths='Deaths'))
View(data.sars.long)

# World sars' long data 
world.sars.long <- data.sars.long %>%
  filter(country == "World")
View(world.sars.long)

g <- ggplot(world.sars.long, aes(date, count, color = type)) +
  geom_line()+
  labs(title = "Number of Cases Worldwide: SARs")+
  xlab("")+
  ylab("")
g


gly.g <- ggplotly(g)
gly.g

gly.plot2
df_sars_lastdate <- data.sars %>%
  filter(date == max.date.s)

df_sars_lastdate
## Current Confirmed Cases
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
3: Unknown or uninitialised column: `Country`. 
4: Unknown or uninitialised column: `Country`. 
5: Unknown or uninitialised column: `Country`. 
6: Unknown or uninitialised column: `Country`. 
7: Unknown or uninitialised column: `Country`. 
data.sars.world <- data.sars %>% filter(country=='World')
View(data.sars.world)
n <- nrow(data.sars.world)
View(data.sars.world)
plot_sars.currconf <- ggplot(data.sars.world, aes(x=date, y=current.confirmed)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='Current Confirmed Cases') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
plot_sars.newconf <- ggplot(data.sars.world, aes(x=date, y=new.confirmed)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='Daily New Confirmed Cases') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
## show two plots side by side
grid.arrange(plot_sars.currconf, plot_sars.newconf, ncol=2)

## a scatter plot with a smoothed line and vertical x-axis labels
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
3: Unknown or uninitialised column: `Country`. 
4: Unknown or uninitialised column: `Country`. 
5: Unknown or uninitialised column: `Country`. 
6: Unknown or uninitialised column: `Country`. 
7: Unknown or uninitialised column: `Country`. 
8: Unknown or uninitialised column: `Country`. 
9: Unknown or uninitialised column: `Country`. 
plot_sars.accdeaths <- ggplot(data.sars.world, aes(x=date, y=deaths)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='Accumulative Deaths') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
plot_sars.accrecov <- ggplot(data.sars.world, aes(x=date, y=recovered)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='Accumulative Recovered Cases') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
plot_sars.newdeaths <- ggplot(data.sars.world, aes(x=date, y=new.deaths)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='New Deaths') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
plot_sars.newrecov <- ggplot(data.sars.world, aes(x=date, y=new.recovered)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='New Recovered Cases') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
## show four plots together, with 2 plots in each row
grid.arrange(plot_sars.accdeaths, plot_sars.accrecov, plot_sars.newdeaths, plot_sars.newrecov, nrow=2)

#Top 20 with gdp
Warning message:
Unknown or uninitialised column: `Country`. 
data.longGDP <- df_gdp %>% gather(key=year, value=GDP, -c(country))
data.top <- data.latest %>% filter(!country %in% c('World', 'Others'))
data.top <- head(data.top,20)
View(data.latest)
data.gdp <- filter(data.longGDP,year=='2020')
df_sars_lastdate_confirmed <- df_sars_lastdate %>%
  select("country", "confirmed") %>%
  rename(sars = "confirmed")
View(df_sars_lastdate_confirmed)
#merge
mergcountry = function(data1,data2){
  data <- merge(x = data1, y = data2, by = "country", all.x = TRUE) 
  return(data)
}
data.top.world <- merge(x = data.top, y = df_gdp2019, by = "country", all.x = TRUE) %>% 
  select(-c(code,rank,new.confirmed,new.deaths,current.confirmed,population)) %>% 
  rename(GDP="GDP (millions of US dollars)")

data.top.world <- merge(x = data.top.world, y = df_healt, by = "country", all.x = TRUE) %>%
  rename(healthcare="healthCareIndex")
#data.top.world <- mergcountry(data.top.world, df_temp)

data.top.world <- merge(x = data.top.world, y = df_pornhub, by = "country", all.x = TRUE) %>%
  rename(Pornhub = "PornhubIndex(%)")

data.top.world <- merge(x = data.top.world, y = df_sars_lastdate_confirmed, by = "country", all.x = TRUE) 


data.top.world <- mergcountry(data.top.world, df_temp)
index <- is.na(data.top.world)
data.top.world[index] <- 0
data.top.world
View(data.top.world)


normalize = function(data){
  #return ((data - min(data,na.rm = TRUE))/(max(data,na.rm = TRUE) - min(data,na.rm = TRUE)))
  z <- scale(data);
  tanh(z/2)
}
norm_data = as.data.frame(apply(data.top.world[,2:13],2,normalize))
corr_data <- norm_data
norm_data$country <- c("Argentina","Bangladesh","Brazil","Chile","Colombia","France","Germany","India","Iran","Italy","Mexico","Pakistan","Peru","Russia","saudi Arabia","South Africa","Spain","Turkey","United Kingdom","US")
#View(norm_data)


norm_data_plot <- select(norm_data,"country","confirm.rate","death.rate","recover.rate","healthcare","Pornhub","GDP","avg_temp", "sars")
norm_data_plot %<>% gather(key=type, value=count, -c(country))
level_order <- factor(norm_data_plot$type, 
                      level = c("sars","Pornhub","GDP","avg_temp","healthcare","recover.rate","death.rate","confirm.rate"))
ggplot(data = norm_data_plot, aes(x=country, y=level_order, fill=count)) + 
  geom_tile() +
  scale_fill_gradient(low = "pink", high = "blue") +
  xlab("") +
  ylab("") +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 90,vjust = 1))+
  theme(
    axis.line = element_blank(),
    axis.ticks = element_blank(),
    panel.grid.minor = element_blank(),
    panel.grid.major = element_blank(),
    panel.border = element_blank(),
    panel.background = element_blank(),
    #legend.position = "none"
  )

#correlation
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
3: Unknown or uninitialised column: `Country`. 
4: Unknown or uninitialised column: `Country`. 
corr_data %<>% select(c(GDP,confirm.rate,death.rate,recover.rate,healthcare,avg_temp,Pornhub, sars))
head(corr_data)
cor(corr_data)
                     GDP confirm.rate  death.rate recover.rate  healthcare    avg_temp     Pornhub
GDP           1.00000000   0.43428937 -0.23113988   -0.3937431  0.25183938 -0.05173909  0.69972226
confirm.rate  0.43428937   1.00000000 -0.34422360   -0.7182585  0.48153104 -0.36431596  0.44060606
death.rate   -0.23113988  -0.34422360  1.00000000    0.1733012 -0.17258588  0.34643395 -0.06882314
recover.rate -0.39374308  -0.71825853  0.17330123    1.0000000 -0.76240251  0.26074521 -0.40581644
healthcare    0.25183938   0.48153104 -0.17258588   -0.7624025  1.00000000 -0.06716339  0.35166155
avg_temp     -0.05173909  -0.36431596  0.34643395    0.2607452 -0.06716339  1.00000000 -0.14789332
Pornhub       0.69972226   0.44060606 -0.06882314   -0.4058164  0.35166155 -0.14789332  1.00000000
sars          0.21121553   0.00498164 -0.04073645   -0.2140829  0.53606331 -0.04676622  0.29417506
                    sars
GDP           0.21121553
confirm.rate  0.00498164
death.rate   -0.04073645
recover.rate -0.21408288
healthcare    0.53606331
avg_temp     -0.04676622
Pornhub       0.29417506
sars          1.00000000
ggcorrplot(cor(corr_data),hc.order = TRUE,
           outline.color = "white",
           colors = c("#6D9EC1","white","#E46726"),
           lab = TRUE)

## Data in US
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
3: Unknown or uninitialised column: `Country`. 
df_us <- tbl(my_db, sql("select * from covidus"))
df_us <- as.data.frame(df_us) 
df_us <- select(df_us, date, state, cases, deaths)

df_us_pop <- tbl(my_db, sql("select * from data_population")) 
df_us_pop <- as.data.frame(df_us_pop)

df_us_gender <- tbl(my_db, sql("select * from data_gender")) 
df_us_gender <- as.data.frame(df_us_gender)

df_us_ethnic <- tbl(my_db, sql("select * from data_ethnic")) 
df_us_ethnic <- as.data.frame(df_us_ethnic)

df_us_lockdown <- tbl(my_db, sql("select * from data_lockdown")) 
df_us_lockdown <- as.data.frame(df_us_lockdown)

df_us_health <- tbl(my_db, sql("select * from data_health")) 
df_us_health <- as.data.frame(df_us_health)

df_us_testing <- tbl(my_db, sql("select * from data_testing")) 
df_us_testing <- as.data.frame(df_us_testing)

#View(df_us_latlong)
df_us <- df_us  %>%
  mutate(date = date %>% mdy()) %>%
  rename("confirmed" = "cases")
#data.us

dates.us <- df_us[,1]
range(dates.us)
[1] "2020-01-21" "2020-12-24"
min.date.us <- min(dates.us)
max.date.us <- max(dates.us)
min.date.txt.us <- min.date.us %>% format('%d %b %Y')
max.date.txt.us <- max.date.us %>% format('%d %b %Y')
day1.us <- min(df_us$date)

data.us.total <- df_us %>% group_by(date) %>%
  summarise(state='US',
            confirmed = sum(confirmed, na.rm=T),
            deaths = sum(deaths, na.rm=T))
`summarise()` ungrouping output (override with `.groups` argument)
#View(data.us.total)

data.us <- df_us
data.us %<>% rbind(data.us.total)
View(data.us)

data.us.long <- data.us %>% 
  gather(key = type, value = count, -c(date, state)) 
#data.us.long

us.total <- data.us.total %>%
  mutate(new.confirmed = ifelse(date == day1, 0, confirmed - lag(confirmed, n=1)),
                new.deaths = ifelse(date == day1, 0, deaths - lag(deaths, n=1)))

us.total %<>% mutate(new.confirmed = ifelse(new.confirmed < 0, 0, new.confirmed),
                 new.deaths = ifelse(new.deaths < 0, 0, new.deaths))
us.total

us.total.long <- us.total %>% 
  gather(key = type, value = count, -c(date, state))

View(us.total.long)
plot_us.cases <- data.us.long %>% filter(state == "US") %>%
There were 15 warnings (use warnings() to see them)
  ggplot(aes(x = date, y = count)) +
  geom_area(aes(fill=type), alpha=0.5) +
  labs(title = paste0("Cumulative cases in US : ", min.date.txt.us, '-', max.date.txt.us, " (Log Scale)"))  +
  scale_y_continuous(trans='log10')+
  scale_fill_manual(values=c('red', 'black'))+
  ylab("")

plot_us.newconf <- us.total.long %>%
  filter(type %in% c("new.confirmed")) %>%
  ggplot(aes(x = date, y = count, color = type)) + 
  geom_line() + 
  labs(title = paste0("Daily confirmed cases in US : ", min.date.txt.us, '-', max.date.txt.us)) +
  xlab("Date") +
  ylab("Confirmed cases")

gly.plot_us.cases <- ggplotly(plot_us.cases)
Transformation introduced infinite values in continuous y-axis
gly.plot_us.newconf <- ggplotly(plot_us.newconf)

gly.plot_us.cases

gly.plot_us.newconf

NA
data.us %<>% mutate(new.confirmed = ifelse(date == day1, NA, confirmed - lag(confirmed, n=1)),
                 new.deaths = ifelse(date == day1, NA, deaths - lag(deaths, n=1)))

data.us %<>% mutate(new.confirmed = ifelse(new.confirmed < 0, 0, new.confirmed),
                 new.deaths = ifelse(new.deaths < 0, 0, new.deaths))
View(data.us)

#data.us.daily <- data.us %>% filter(state == "US")
#View(data.us.daily)

data.us.pop <- df_us_pop %>% select(State, Population) %>%
  rename("state" = "State") 
#data.us.pop

data.us.gender <- df_us_gender %>% select(State, Male, Female) %>%
  rename("state" = "State") 
data.us.gender

data.us.latest <- data.us %>%
  filter(date == max.date.us) %>% 
  merge(data.us.pop, by = "state", all.x = T) %>%
  merge(data.us.gender, by = "state", all.x = T)
#View(data.us.latest)

data.us.latest$Population[data.us.latest$state == "US"] <- sum(data.us.pop$Population)
data.us.latest %<>% mutate(ranking = dense_rank(desc(confirmed)),
                           confirmed.rate = (100 * confirmed / Population) %>% round(2),
                           death.rate = (100 * deaths / confirmed) %>% round(2),
                           Male.confirmed = (Male * confirmed) %>% round(0),
                           Female.confirmed = Female * confirmed %>% round(0),
                           Male.deaths = Male * deaths,
                           Female.deaths = Female * deaths) %>%
  arrange(ranking) 

top.us <- data.us.latest[,1]
top.us
 [1] "US"                       "California"               "Texas"                   
 [4] "Florida"                  "Illinois"                 "New York"                
 [7] "Ohio"                     "Georgia"                  "Pennsylvania"            
[10] "Tennessee"                "Michigan"                 "Wisconsin"               
[13] "North Carolina"           "Indiana"                  "Arizona"                 
[16] "New Jersey"               "Minnesota"                "Missouri"                
[19] "Massachusetts"            "Alabama"                  "Virginia"                
[22] "Colorado"                 "Louisiana"                "South Carolina"          
[25] "Iowa"                     "Oklahoma"                 "Maryland"                
[28] "Utah"                     "Kentucky"                 "Washington"              
[31] "Kansas"                   "Nevada"                   "Arkansas"                
[34] "Mississippi"              "Connecticut"              "Nebraska"                
[37] "Idaho"                    "New Mexico"               "Oregon"                  
[40] "Puerto Rico"              "South Dakota"             "North Dakota"            
[43] "Rhode Island"             "Montana"                  "West Virginia"           
[46] "Delaware"                 "Alaska"                   "Wyoming"                 
[49] "New Hampshire"            "District of Columbia"     "Maine"                   
[52] "Hawaii"                   "Guam"                     "Vermont"                 
[55] "Virgin Islands"           "Northern Mariana Islands"
View(top.us)


data.us.latest
NA
# List of top 20 state
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
3: Unknown or uninitialised column: `Country`. 
k <- 20
data.us.top <- data.us.latest %>%
  filter(ranking <= k+1) %>% 
  arrange(ranking) 
View(data.us.top)

us.state.top <- data.us.top %>% pull(state) %>% as.character()
us.state.top  %>% setdiff('US') %>% print()
 [1] "California"     "Texas"          "Florida"        "Illinois"       "New York"       "Ohio"          
 [7] "Georgia"        "Pennsylvania"   "Tennessee"      "Michigan"       "Wisconsin"      "North Carolina"
[13] "Indiana"        "Arizona"        "New Jersey"     "Minnesota"      "Missouri"       "Massachusetts" 
[19] "Alabama"        "Virginia"      
# confirmed rate & death rate of top 20 state
g.rate <- data.us.latest %>% filter(state %in% us.state.top & state != "US") %>%
  select(state, confirmed.rate, death.rate, ranking) %>%
  gather(key = Type, value = Percent, -c(state, ranking)) %>%
  ggplot(aes(x=reorder(state, -desc(ranking)), y=Percent, fill = Percent)) +
  geom_bar(stat='identity') +
  scale_fill_gradient(low = "#ebbc62", high = "#b42006") +
  geom_text(aes(label=Percent, y=Percent), size=3, vjust=0) +
  xlab('') + ylab('') +
  labs(title=paste0('Confirmed Rate & Death Rate of Top 20 State in US')) +
  #scale_fill_continuous(name='State', labels=aes(Percent)) +
  theme(legend.title=element_blank(),
        legend.position='none',
        plot.title=element_text(size=13),
        axis.text=element_text(size=8),
        axis.text.x=element_text(angle=45, hjust=1)) +
  facet_wrap(~Type, ncol=1, scales='free_y') 

g.rate

us.confirmed.num <- data.us.top$confirmed[data.us.top$state == "US"] %>% as.numeric()
Warning message:
Unknown or uninitialised column: `Country`. 
us.confirmed.num
[1] 18706036
data.us.top %<>% mutate(confirmed.per.us = (confirmed * 100 / us.confirmed.num) %>% round(1))
data.us.top

g.us.top1 <- data.us.top %>%
  filter(state != "US") %>%
  select(state, confirmed, confirmed.per.us, ranking) %>%
  gather(key = Type, value = count, -c(state, ranking, confirmed.per.us)) %>%
  
  ggplot(aes(fill = count, y = count, x = reorder(state, -desc(ranking)))) + 
  geom_bar(position = "dodge", stat = "identity") +
  labs(title = "20 state in US with most confirmed cases") +
  scale_fill_gradient(low = "#ebbc62", high = "#b42006") +
  xlab("") + 
  ylab("Confirmed Cases") +
  geom_text(aes(label=paste0(confirmed.per.us, "%")), size=3, vjust=-0.5) +
  theme(axis.text.x = element_text(angle = 90,vjust = 0.5))+
  theme(
    axis.line = element_blank(),
    axis.ticks = element_blank(),
    panel.grid.minor = element_blank(),
    panel.grid.major = element_blank(),
    panel.border = element_blank(),
    panel.background = element_blank(),
    #legend.position = "none"
  )

g.us.top1


gly.us.top1 <- ggplotly(g.us.top1)
gly.us.top1

NA
data.us.latest.corr <- df_us %>%filter(date==max.date.us)
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
View(data.us.latest.corr)

data.us.latest.corr %<>% mutate(ranking = dense_rank(desc(confirmed))) %>% arrange(ranking)
top.us <- data.us.latest.corr[,2]
#top.us
# List of top 20 state
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
k <- 19
data.us.latest.top <- data.us.latest.corr %>%
    filter(ranking <= k+1) %>% 
    arrange(ranking) 
View(data.us.latest.top)
us.state.top <- data.us.latest.top %>% pull(state) %>% as.character()
us.state.top  %>% setdiff('US') %>% print()
 [1] "California"     "Texas"          "Florida"        "Illinois"       "New York"       "Ohio"          
 [7] "Georgia"        "Pennsylvania"   "Tennessee"      "Michigan"       "Wisconsin"      "North Carolina"
[13] "Indiana"        "Arizona"        "New Jersey"     "Minnesota"      "Missouri"       "Massachusetts" 
[19] "Alabama"        "Virginia"      
View(us.state.top)
#gender in us
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
3: Unknown or uninitialised column: `Country`. 
4: Unknown or uninitialised column: `Country`. 
df_us_gender <- tbl(my_db, sql("select * from data_gender"))
df_us_gender <- as.data.frame(df_us_gender)
df_us_gender <- select(df_us_gender,c("State","Male","Female"))
df_us_gender <- rename(df_us_gender,"state"="State")
df_us_gender

#population in us
df_us_pop <- tbl(my_db, sql("select * from data_population"))
df_us_pop <- as.data.frame(df_us_pop)
df_us_pop <- select(df_us_pop,c("State","Population"))
df_us_pop <- rename(df_us_pop,"state"="State")
df_us_pop

#lockdown in us
df_us_lockdown <- tbl(my_db, sql("select * from data_lockdown"))
df_us_lockdown <- as.data.frame(df_us_lockdown)
df_us_lockdown <- select(df_us_lockdown,c("State","Day lockdown"))
df_us_lockdown <- rename(df_us_lockdown,"state"="State")
df_us_lockdown

#GDP in us
df_us_gdp <- tbl(my_db, sql("select * from us_gdp"))
df_us_gdp  <- as.data.frame(df_us_gdp )
df_us_gdp  <- select(df_us_gdp ,c("State","GDPs"))
df_us_gdp  <- rename(df_us_gdp ,"state"="State")
df_us_gdp 

#homeless in us
df_us_homeless <- tbl(my_db, sql("select * from us_homeless"))
df_us_homeless <- as.data.frame(df_us_homeless)
df_us_homeless <- select(df_us_homeless,c("State","Homeless"))
df_us_homeless <- rename(df_us_homeless,"state"="State")
df_us_homeless

#View(df_us)
#merge
mergcountry = function(data1,data2){
  data <- merge(x = data1, y = data2, by = "state", all.x = TRUE) 
  return(data)
}

data.top.state <- data.us.latest.top %>% filter(state != "US") %>%
  select(state, confirmed, deaths)
#View(data.top.state)

df_Allus <- mergcountry(data.top.state, df_us_gender)

df_Allus <- mergcountry(df_Allus, df_us_pop) 

df_Allus <- mergcountry(df_Allus, df_us_lockdown) 

df_Allus <- mergcountry(df_Allus, df_us_gdp)

df_Allus <- mergcountry(df_Allus, df_us_homeless)

#View(df_Allus)
index <- is.na(df_Allus)
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
df_Allus[index] <- 0
View(df_Allus)

normalize = function(data){
  #return ((data - min(data,na.rm = TRUE))/(max(data,na.rm = TRUE) - min(data,na.rm = TRUE)))
  z <- scale(data);
  tanh(z/2)
}
Allus = as.data.frame(apply(df_Allus[,2:9],2,normalize))
corr_dataUS <- Allus 

View(corr_dataUS)
Allus$state <- c(df_Allus$state)
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
3: Unknown or uninitialised column: `Country`. 
View(Allus)

corr_dataUS <- rename(corr_dataUS ,"Daylockdown" = "Day lockdown")
Allus_plot <- select(Allus,"state","confirmed","deaths","Male","Female","Population","Day lockdown","GDPs","Homeless")
Allus_plot %<>% gather(key=type, value=count, -c(state))
level_order <- factor(Allus_plot$type, 
                      level = c("confirmed","deaths","Male","Female","Population","Day lockdown","GDPs","Homeless"))
ggplot(data = Allus_plot, aes(x=state, y=level_order, fill=count)) + 
  geom_tile() +
  scale_fill_gradient(low = "pink", high = "blue") +
  xlab("") +
  ylab("") +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 90,vjust = 1))+
  theme(
    axis.line = element_blank(),
    axis.ticks = element_blank(),
    panel.grid.minor = element_blank(),
    panel.grid.major = element_blank(),
    panel.border = element_blank(),
    panel.background = element_blank(),
    #legend.position = "none"
  ) +labs(title='The heatmap of COVID-19 infections in US')

#correlation
corr_dataUS %<>% select(c(confirmed,deaths,Male,Female,Population,Daylockdown,GDPs,Homeless))
head(corr_dataUS)
cor(corr_dataUS)
               confirmed       deaths         Male       Female   Population Daylockdown        GDPs
confirmed    1.000000000  0.792654639  0.105367061 -0.005945092  0.959941301  -0.2058259  0.91047827
deaths       0.792654639  1.000000000  0.008041947  0.017335533  0.852691131  -0.3797468  0.88497061
Male         0.105367061  0.008041947  1.000000000 -0.877329594 -0.009961687  -0.2898630 -0.02437981
Female      -0.005945092  0.017335533 -0.877329594  1.000000000  0.069101784   0.3629328  0.02235864
Population   0.959941301  0.852691131 -0.009961687  0.069101784  1.000000000  -0.2155126  0.97052821
Daylockdown -0.205825865 -0.379746786 -0.289863040  0.362932815 -0.215512639   1.0000000 -0.36113782
GDPs         0.910478267  0.884970609 -0.024379811  0.022358644  0.970528214  -0.3611378  1.00000000
Homeless     0.791615898  0.796426563 -0.080155515  0.151488777  0.863178014  -0.3573560  0.89961070
               Homeless
confirmed    0.79161590
deaths       0.79642656
Male        -0.08015552
Female       0.15148878
Population   0.86317801
Daylockdown -0.35735597
GDPs         0.89961070
Homeless     1.00000000
ggcorrplot(cor(corr_dataUS),hc.order = TRUE,
           outline.color = "white",
           colors = c("#6D9EC1","white","#E46726"),
           lab = TRUE)

#Covid_Thailand
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
3: Unknown or uninitialised column: `Country`. 
4: Unknown or uninitialised column: `Country`. 
5: Unknown or uninitialised column: `Country`. 
6: Unknown or uninitialised column: `Country`. 
7: Unknown or uninitialised column: `Country`. 
8: Unknown or uninitialised column: `Country`. 
9: Unknown or uninitialised column: `Country`. 
df_thai <- tbl(my_db, sql("select * from covid19_Thailand"))
df_thai <- as.data.frame(df_thai)
View(df_thai)
#clean Covid_Thailand
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
3: Unknown or uninitialised column: `Country`. 
dates.th <- df_thai[,2]%>% mdy()
range(dates.th)
[1] "2020-01-12" "2021-01-15"
min.date.th <- min(dates.th)
max.date.th <- max(dates.th)
min.date.txt.th <- min.date.th %>% format('%d %b %Y')
max.date.txt.th <- max.date.th %>% format('%d %b %Y')
df_thai$announce_date <- mdy(df_thai$announce_date)
Warning message:
Unknown or uninitialised column: `Country`. 
df_thai$notification_date <- mdy(df_thai$notification_date)
df_thai
df_thai <- df_thai %>% select(!No.) %>% select(!notification_date) %>% 
Warning message:
Unknown or uninitialised column: `Country`. 
  group_by(announce_date)
df_thai
# Total confirmed cases in Thailand
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
data.thai.count <- df_thai %>%
  select(announce_date) %>%
  summarise(comfirmed = n()) %>% as.data.frame()
`summarise()` ungrouping output (override with `.groups` argument)
View(data.thai.count)
data.thai.count$cumulative_confirmed <- cumsum(data.thai.count[, 2])
View(data.thai.count)
## Thai Confirmed Cases (Jan 2020 - Jan 2021
Warning message:
Unknown or uninitialised column: `Country`. 
plot1 <- ggplot(data.thai.count, aes(x=announce_date, y=cumulative_confirmed)) +
  geom_point() + geom_smooth() +
  xlab(" ") + ylab("Count") + labs(title='Thai Cumulative Confirmed Cases (Jan 2020 - Jan 2021)') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.thai.count, aes(x=announce_date, y=comfirmed)) +
  geom_point() + geom_smooth() +
    xlab(" ") + ylab("Count")+ labs(title='Thai Confirmed Cases (Jan 2020 - Jan 2021 log scale)') +
   theme(axis.text.x=element_text(angle=45, hjust=1))+scale_y_continuous(trans='log10')
## show two plots side by side
grid.arrange(plot1, plot2, ncol=1)

# Confirmed cases divided by sex (gender)
Warning message:
Unknown or uninitialised column: `Country`. 
df_thai$sex[df_thai$sex == ""] <- "Unknown"
data.thai.gender <- df_thai %>%
  group_by(sex) %>%
  summarise(count = n()) %>%
  mutate(percent = (count / sum(count) * 100) %>% round(2)) %>%
  filter(percent>1)%>%
  #mutate(pos = cumsum(percent) - 0.5*percent) %>%
  arrange(desc(percent))
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.gender
data.thai.gender$sex <- factor(data.thai.gender$sex, levels = as.character(data.thai.gender$sex))
Warning message:
Unknown or uninitialised column: `Country`. 
data.thai.gender$sex
[1] Female Male   Unknow
Levels: Female Male Unknow
g.th.gender <- data.thai.gender %>% 
  ggplot(aes(x = "", y = percent, fill = sex)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar("y") +
  theme_void() +
  labs(title='Gender of Thai Confirmed Cases (Jan 2020 - Jan 2021)')+
  geom_text(aes(label = paste0(percent, "%")), color = "white", size = 5, position = position_stack(vjust = 0.5)) +
  guides(fill = guide_legend(reverse = TRUE)) 
g.th.gender

# Confirmed cases divided by province_of_onset
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
3: Unknown or uninitialised column: `Country`. 
data.thai.onset <- df_thai %>%
    group_by(province_of_onset) %>%
    summarise(count = n()) %>%
    arrange(desc(count))%>%
    rename(onset="count")%>%
    rename(province="province_of_onset")
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.onset$province[data.thai.onset$province == ""] <- "Unknown"
data.thai.onset
# Confirmed cases divided by province_of_isolation 
data.thai.isolation <- df_thai %>%
    group_by(province_of_isolation) %>%
    summarise(count = n()) %>%
    arrange(desc(count))%>%
    rename(isolation ="count")%>%
    rename(province="province_of_isolation")
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.isolation$province[data.thai.isolation$province == ""] <- "Unknown"
data.thai.isolation
data.province <- merge(x = data.thai.onset, y = data.thai.isolation, by = "province", all.x = TRUE) 
data.province <- data.province%>%filter(onset>125)
data.province
data.province.long <- data.province %>%
    gather(key=type, value=count, -c(province))
data.province.long
ggplot(data.province.long,aes(x=province,y=count))+
    geom_bar(stat = "identity",position = "dodge",aes(fill=type))+
    labs(title = "Province of onset and Province of isolation in Thailand")+
    theme(legend.title=element_blank(),
                  #legend.position='none',
                  plot.title=element_text(size=12),
                  axis.text=element_text(size=10),
                  axis.text.x=element_text(angle=45, hjust=1))+ xlab('') + ylab('Count')

# Confirmed cases divided by risk
data.thai.risk <- df_thai %>%
  group_by(risk) %>%
  summarise(count = n()) %>%
  arrange(desc(count))
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.risk
# Confirmed cases divided by risk
data.thai.risk <- df_thai %>%
  group_by(risk) %>%
  summarise(count = n()) %>%
  mutate(percent = (count / sum(count) * 100) %>% round(2)) %>%
  filter(percent>0.76) %>%
  arrange(desc(percent)) 
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.risk
data.thai.risk$risk[data.thai.risk$risk == "C"] <- "Close contact with the patient"
data.thai.risk$risk[data.thai.risk$risk == "CGustDr Samut SaGGAF"] <- "Cluster Samut Sakhon"
data.thai.risk$risk[data.thai.risk$risk == "F"] <- "State Quarantine"
data.thai.risk$risk[data.thai.risk$risk == "G"] <- "Go to a crowded place"
data.thai.risk$risk[data.thai.risk$risk == "A"] <- "People travelling from abroad"
data.thai.risk$risk[data.thai.risk$risk == "UFGFAwF"] <- "Unknown"
data.thai.risk$risk[data.thai.risk$risk == "D"] <- "Career at risk"
data.thai.risk$risk[data.thai.risk$risk == "CGustDr RayAFg"] <- "Cluster Rayong"
data.thai.risk$risk[data.thai.risk$risk == "CGustDr Pattaya CasCFA"] <- "Cluster Pattaya Casino"
data.thai.risk$risk[data.thai.risk$risk == "CGustDr AFg TGAFg"] <- "Cluster Ang Thong"
data.thai.risk
data.thai.risk$risk <- factor(data.thai.risk$risk, levels = as.character(data.thai.risk$risk))
data.thai.risk$risk
[1] Cluster Samut Sakhon           Close contact with the patient Go to a crowded place         
[4] State Quarantine               People travelling from abroad  Unknown                       
[7] Cluster Rayong                 Career at risk                 Cluster Pattaya Casino        
9 Levels: Cluster Samut Sakhon Close contact with the patient Go to a crowded place ... Cluster Pattaya Casino
g.th.risk <- data.thai.risk %>% 
  ggplot(aes(x = "", y = percent, fill = risk)) +
  geom_bar(stat = "identity", width = 0.5) +
  coord_polar("y") +
  theme_void() +
  labs(title='Risk of Thai Confirmed Cases(Jan 2020 - Jan 2021)')+
  geom_text(aes(label = paste0(percent, "%")), color = "Black", size = 3, position = position_stack(vjust = 0.5)) +
  guides(fill = guide_legend(reverse = TRUE)) 
g.th.risk

# Confirmed cases divided by age
Warning message:
Unknown or uninitialised column: `Country`. 
data.thai.age <- df_thai %>%
  group_by(age,sex) %>% 
  filter(age != 0)%>%
  summarise(count = n()) %>%
  arrange(desc(count))
`summarise()` regrouping output by 'age' (override with `.groups` argument)
data.thai.age
data.thai.age <- data.thai.age# %>% filter(sex != "Unknown")
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
3: Unknown or uninitialised column: `Country`. 
ggplot(data.thai.age,aes(x=age,y=count,fill=sex))+geom_bar(stat = "identity")+
      labs(title='Age of Thai Confirmed Cases (Stack)')+guides(fill=guide_legend(reverse = T))

# Confirmed cases divided by nationality
Warning messages:
1: Unknown or uninitialised column: `Country`. 
2: Unknown or uninitialised column: `Country`. 
3: Unknown or uninitialised column: `Country`. 
4: Unknown or uninitialised column: `Country`. 
5: Unknown or uninitialised column: `Country`. 
data.thai.nationality <- df_thai %>%
  group_by(nationality) %>%
  summarise(count = n()) %>%
  filter(count > 11)%>%
  arrange(desc(count))
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.nationality$nationality[data.thai.nationality$nationality == "????????"] <- "Unknown"
data.thai.nationality$nationality[data.thai.nationality$nationality == ""] <- "Unknown"
data.thai.nationality$nationality[data.thai.nationality$nationality == "Burma"] <- "Myanmar"
data.thai.nationality
ggplot(data.thai.nationality)+ 
  geom_bar(aes(x=nationality,y=count,fill = count),stat = "identity",show.legend = F)+
    ggtitle('Nationality of Thai Confirmed Cases (log scale)')+
    theme(plot.title = element_text(hjust = 0.5))+
    theme(axis.text = element_text(size = 10),
            axis.title = element_text(size = 10),
            plot.title = element_text(size = 10))+
    xlab("")+ylab("Number of Nationality")+
   scale_y_continuous(trans='log10')+coord_flip()+scale_fill_gradient(low="blue",high = "red")

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpDQojaW5zdGFsbC5wYWNrYWdlcygiZ3JpZCIpDQojaW5zdGFsbC5wYWNrYWdlcygiTUxSTVBBIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJkcHJlcCIpDQojaW5zdGFsbC5wYWNrYWdlcygibm9ybWFsciIpDQojaW5zdGFsbC5wYWNrYWdlcygiZ2djb3JycGxvdCIpDQojaW5zdGFsbC5wYWNrYWdlcygiUkNvbG9yQnJld2VyIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJyZ2RhbCIpDQojaW5zdGFsbC5wYWNrYWdlcygianNvbmxpdGUiKQ0KI2luc3RhbGwucGFja2FnZXMoIlJDb2xvckJyZXdlciIpDQojaW5zdGFsbC5wYWNrYWdlcygicmVhZHIiKQ0KU3lzLnNldGxvY2FsZSgiTENfQUxMIiwgIkVuZ2xpc2giKQ0KU3lzLnNldGVudigiTEFOR1VBR0UiPSJFbiIpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkobWFncml0dHIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkobm9ybWFscikNCmxpYnJhcnkoZ2djb3JycGxvdCkNCmxpYnJhcnkobGVhZmxldCkNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpsaWJyYXJ5KHJlYWRyKQ0KDQojPz9zcmNfbXlzcWwNCm15X2RiIDwtIHNyY19teXNxbCgNCiAgZGJuYW1lID0gImNvdmlkIiwNCiAgaG9zdCA9ICIxMjcuMC4wLjEiLA0KICB1c2VyID0gInJvb3QiLA0KICBwYXNzd29yZCA9ICIxMjM0Ig0KKQ0KbXlfZGINCg0KIyNpbXBvcnQgZGF0YQ0KZGZfY29uZiA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBjb3ZpZDE5X2NvbmZpcm1lZCIpKQ0KZGZfY29uZiA8LSBhcy5kYXRhLmZyYW1lKGRmX2NvbmYpDQpkZl9jb25mDQpkZl9kZWF0aHMgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gY292aWQxOV9kZWF0aHMiKSkNCmRmX2RlYXRocyA8LSBhcy5kYXRhLmZyYW1lKGRmX2RlYXRocykNCmRmX2RlYXRocw0KZGZfcmVjb3ZlciA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBjb3ZpZDE5X3JlY292ZXJlZCIpKQ0KZGZfcmVjb3ZlciA8LSBhcy5kYXRhLmZyYW1lKGRmX3JlY292ZXIpDQpkZl9yZWNvdmVyDQpgYGANCg0KYGBge3J9DQojI2NoZWNrIHRoZSB0aW1lIGZyYW1lIG9mIHRoZSBkYXRhDQpuLmNvbCA8LSBuY29sKGRmX2NvbmYpDQpkYXRlcyA8LSBuYW1lcyhkZl9jb25mKVs1Om4uY29sXSU+JSBtZHkoKQ0KcmFuZ2UoZGF0ZXMpDQptaW4uZGF0ZSA8LSBtaW4oZGF0ZXMpDQptYXguZGF0ZSA8LSBtYXgoZGF0ZXMpDQptaW4uZGF0ZS50eHQgPC0gbWluLmRhdGUgJT4lIGZvcm1hdCgnJWQgJWIgJVknKQ0KbWF4LmRhdGUudHh0IDwtIG1heC5kYXRlICU+JSBmb3JtYXQoJyVkICViICVZJykNCmBgYA0KYGBge3J9DQojY2xlYW4gZGF0YQ0KY2xlYW5EYXRhIDwtIGZ1bmN0aW9uKGRhdGEpIHsNCiAgIyMgcmVtb3ZlIHNvbWUgY29sdW1ucw0KICBkYXRhICU8PiUgc2VsZWN0KC1jKFByb3ZpbmNlLlN0YXRlLCBMYXQsIExvbmcpKSAlPiUgcmVuYW1lKGNvdW50cnk9Q291bnRyeS5SZWdpb24pDQogICMjIGNvbnZlcnQgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0DQogIGRhdGEgJTw+JSBnYXRoZXIoa2V5PWRhdGUsIHZhbHVlPWNvdW50LCAtY291bnRyeSkNCiAgIyMgY29udmVydCBmcm9tIGNoYXJhY3RlciB0byBkYXRlDQogIGRhdGEgJTw+JSBtdXRhdGUoZGF0ZSA9IGRhdGUgJT4lIG1keSgpKQ0KICAjIyBhZ2dyZWdhdGUgYnkgY291bnRyeQ0KICBkYXRhICU8PiUgZ3JvdXBfYnkoY291bnRyeSwgZGF0ZSkgJT4lIHN1bW1hcmlzZShjb3VudD1zdW0oY291bnQsIG5hLnJtPVQpKSAlPiUgYXMuZGF0YS5mcmFtZSgpDQogIHJldHVybihkYXRhKQ0KfQ0KIyMgY2xlYW4gdGhlIHRocmVlIGRhdGEgc2V0cw0KZGF0YS5jb25maXJtZWQgPC0gZGZfY29uZiAlPiUgY2xlYW5EYXRhKCkgJT4lIHJlbmFtZShjb25maXJtZWQ9Y291bnQpDQpkYXRhLmRlYXRocyA8LSBkZl9kZWF0aHMgJT4lIGNsZWFuRGF0YSgpICU+JSByZW5hbWUoZGVhdGhzPWNvdW50KQ0KZGF0YS5yZWNvdmVyZWQgPC0gZGZfcmVjb3ZlciAlPiUgY2xlYW5EYXRhKCkgJT4lIHJlbmFtZShyZWNvdmVyZWQ9Y291bnQpDQpkYXRhIDwtIGRhdGEuY29uZmlybWVkICU+JSBtZXJnZShkYXRhLmRlYXRocywgYWxsPVQpICU+JSBtZXJnZShkYXRhLnJlY292ZXJlZCwgYWxsPVQpDQpkYXRhDQojIyBjb3VudHJpZXMvcmVnaW9ucyB3aXRoIGNvbmZpcm1lZCBjYXNlcywgZXhjbC4gY3J1aXNlIHNoaXBzDQpjb3VudHJpZXMgPC0gZGF0YSAlPiUgcHVsbChjb3VudHJ5KSAlPiUgc2V0ZGlmZignQ3J1aXNlIFNoaXAnKQ0KZGF0YQ0KYGBgDQoNCmBgYHtyfQ0KZGF0YS53b3JsZCA8LSBkYXRhICU+JSBncm91cF9ieShkYXRlKSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50cnk9J1dvcmxkJywNCiAgICAgICAgICAgIGNvbmZpcm1lZCA9IHN1bShjb25maXJtZWQsIG5hLnJtPVQpLA0KICAgICAgICAgICAgZGVhdGhzID0gc3VtKGRlYXRocywgbmEucm09VCksDQogICAgICAgICAgICByZWNvdmVyZWQgPSBzdW0ocmVjb3ZlcmVkLCBuYS5ybT1UKSkNCmRhdGEgJTw+JSByYmluZChkYXRhLndvcmxkKQ0KZGF0YQ0KZGF0YSAlPD4lIG11dGF0ZShjdXJyZW50LmNvbmZpcm1lZCA9IGNvbmZpcm1lZCAtIGRlYXRocyAtIHJlY292ZXJlZCkNClZpZXcoZGF0YSkNCmBgYA0KDQoNCg0KYGBge3J9DQojIFdvcmxkIGNvbmZpcm1lZCBjYXNlcyBtYXANCndvcmxkLmxvY2F0aW9uIDwtIGRmX2NvbmYNCiNWaWV3KHdvcmxkLmxvY2F0aW9uICkNCndvcmxkLmxvY2F0aW9uJGNvbmZpcm1lZCA8LSB3b3JsZC5sb2NhdGlvbiBbLCBuY29sKHdvcmxkLmxvY2F0aW9uICldDQp3b3JsZC5sb2NhdGlvbiAgJTw+JSBzZWxlY3QoYyhDb3VudHJ5LlJlZ2lvbiwgUHJvdmluY2UuU3RhdGUsIExhdCwgTG9uZywgY29uZmlybWVkKSkgJT4lDQogIG11dGF0ZSh0eHQ9cGFzdGUwKENvdW50cnkuUmVnaW9uLCAnIC0gJywgUHJvdmluY2UuU3RhdGUsICc6ICcsIGNvbmZpcm1lZCkpDQptYXAuY29uZmlybWVkIDwtIGxlYWZsZXQod2lkdGg9MTIwMCwgaGVpZ2h0PTgwMCkgJT4lIGFkZFRpbGVzKCkNCiMgY2lyY2xlIG1hcmtlciAodW5pdHMgaW4gcGl4ZWxzKQ0KbWFwLmNvbmZpcm1lZCAlPD4lIGFkZENpcmNsZU1hcmtlcnMoeCRMb25nLCB4JExhdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICMgcmFkaXVzPTIrbG9nMih4JGNvbmZpcm1lZCksDQogICAgICAgICAgICAgICAgICAgICAgICByYWRpdXM9MC4wMipzcXJ0KHgkY29uZmlybWVkKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHN0cm9rZT1GLA0KICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9J3JlZCcsIGZpbGxPcGFjaXR5PTAuMywNCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPXgkdHh0KQ0KIyB3b3JsZA0KbWFwLmNvbmZpcm1lZA0KYGBgDQoNCmBgYHtyfQ0KIyMgQ2hpbmENCm1hcC5jb25maXJtZWQgJT4lIHNldFZpZXcoOTUsIDM1LCB6b29tPTQpDQojIyBBdXN0cmFsaWEgYW5kIE5ldyBaZWFsYW5kDQptYXAuY29uZmlybWVkICU+JSBzZXRWaWV3KDEzNSwgLTI3LCB6b29tPTQpDQojIyBVUyBhbmQgQ2FuYWRhDQptYXAuY29uZmlybWVkICU+JSBzZXRWaWV3KC0xMDUsIDQwLCB6b29tPTQpDQojIyBFdXJvcGUNCm1hcC5jb25maXJtZWQgJT4lIHNldFZpZXcoMTAsIDUwLCB6b29tPTQpDQpgYGANCg0KYGBge3J9DQojIG5ldy5jb25maXJtZWQgbmV3LmRlYXRocyBuZXcucmVjb3ZlcmVkDQpkYXRhICU8PiUgYXJyYW5nZShjb3VudHJ5LCBkYXRlKQ0KbiA8LSBucm93KGRhdGEpDQpkYXkxIDwtIG1pbihkYXRhJGRhdGUpDQpkYXRhICU8PiUgbXV0YXRlKG5ldy5jb25maXJtZWQgPSBpZmVsc2UoZGF0ZSA9PSBkYXkxLCBOQSwgY29uZmlybWVkIC0gbGFnKGNvbmZpcm1lZCwgbj0xKSksDQogICAgICAgICAgICAgICAgIG5ldy5kZWF0aHMgPSBpZmVsc2UoZGF0ZSA9PSBkYXkxLCBOQSwgZGVhdGhzIC0gbGFnKGRlYXRocywgbj0xKSksDQogICAgICAgICAgICAgICAgIG5ldy5yZWNvdmVyZWQgPSBpZmVsc2UoZGF0ZSA9PSBkYXkxLCBOQSwgcmVjb3ZlcmVkIC0gbGFnKHJlY292ZXJlZCwgbj0xKSkpDQpkYXRhICU8PiUgbXV0YXRlKG5ldy5jb25maXJtZWQgPSBpZmVsc2UobmV3LmNvbmZpcm1lZCA8IDAsIDAsIG5ldy5jb25maXJtZWQpLA0KICAgICAgICAgICAgICAgICBuZXcuZGVhdGhzID0gaWZlbHNlKG5ldy5kZWF0aHMgPCAwLCAwLCBuZXcuZGVhdGhzKSwNCiAgICAgICAgICAgICAgICAgbmV3LnJlY292ZXJlZCA9IGlmZWxzZShuZXcucmVjb3ZlcmVkIDwgMCwgMCwgbmV3LnJlY292ZXJlZCkpDQojIyBkZWF0aCByYXRlIGJhc2VkIG9uIHRvdGFsIGRlYXRocyBhbmQgcmVjb3ZlcmVkIGNhc2VzDQpkYXRhICU8PiUgbXV0YXRlKHJhdGUudXBwZXIgPSAoMTAwICogZGVhdGhzIC8gKGRlYXRocyArIHJlY292ZXJlZCkpICU+JSByb3VuZCgxKSwNCiAgICAgICAgICAgICAgICAgcmF0ZS51cHBlciA9IGlmZWxzZShpcy5uYW4ocmF0ZS51cHBlciksIDAsIHJhdGUudXBwZXIpKQ0KDQojIyBsb3dlciBib3VuZDogZGVhdGggcmF0ZSBiYXNlZCBvbiB0b3RhbCBjb25maXJtZWQgY2FzZXMNCmRhdGEgJTw+JSBtdXRhdGUocmF0ZS5sb3dlciA9ICgxMDAgKiBkZWF0aHMgLyBjb25maXJtZWQpICU+JSByb3VuZCgxKSwNCiAgICAgICAgICAgICAgICAgcmF0ZS5sb3dlciA9IGlmZWxzZShpcy5uYW4ocmF0ZS5sb3dlciksIDAsIHJhdGUubG93ZXIpKQ0KDQojIyBkZWF0aCByYXRlIGJhc2VkIG9uIHRoZSBudW1iZXIgb2YgZGVhdGgvcmVjb3ZlcmVkIG9uIGV2ZXJ5IHNpbmdsZSBkYXkNCmRhdGEgJTw+JSBtdXRhdGUocmF0ZS5kYWlseSA9ICgxMDAgKiBuZXcuZGVhdGhzIC8gKG5ldy5kZWF0aHMgKyBuZXcucmVjb3ZlcmVkKSkgJT4lIHJvdW5kKDEpLA0KICAgICAgICAgICAgICAgICByYXRlLmRhaWx5ID0gaWZlbHNlKGlzLm5hbihyYXRlLmRhaWx5KSwgMCwgcmF0ZS5kYWlseSkpDQoNClZpZXcoZGF0YSkNCmBgYA0KDQpgYGB7cn0NCiMjIGNvbnZlcnQgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0DQpkYXRhLmxvbmcgPC0gZGF0YSAlPiUNCiAgc2VsZWN0KGMoY291bnRyeSwgZGF0ZSwgY29uZmlybWVkLCBjdXJyZW50LmNvbmZpcm1lZCwgcmVjb3ZlcmVkLCBkZWF0aHMpKSAlPiUNCiAgZ2F0aGVyKGtleT10eXBlLCB2YWx1ZT1jb3VudCwgLWMoY291bnRyeSwgZGF0ZSkpDQojIyBzZXQgZmFjdG9yIGxldmVscyB0byBzaG93IHRoZW0gaW4gYSBkZXNpcmFibGUgb3JkZXINCmRhdGEubG9uZyAlPD4lIG11dGF0ZSh0eXBlPXJlY29kZV9mYWN0b3IodHlwZSwgY29uZmlybWVkPSdUb3RhbCBDb25maXJtZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJyZW50LmNvbmZpcm1lZD0nQ3VycmVudCBDb25maXJtZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWNvdmVyZWQ9J1JlY292ZXJlZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlYXRocz0nRGVhdGhzJykpDQpWaWV3KGRhdGEubG9uZykNCmBgYA0KDQpgYGB7cn0NCiMjTnVtYmVyIG9mIGNhc2UgV29ybGQNCndvcmxkIDwtIGZpbHRlcihkYXRhLmxvbmcsY291bnRyeSA9PSAnV29ybGQnKQ0Kd29ybGQNCnBsb3Rfd29ybGQuYXJlYSA8LSB3b3JsZCAlPiUgZmlsdGVyKHR5cGUgIT0gJ1RvdGFsIENvbmZpcm1lZCcpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1jb3VudCkpICsNCiAgZ2VvbV9hcmVhKGFlcyhmaWxsPXR5cGUpLCBhbHBoYT0wLjUpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ051bWJlcnMgb2YgQ2FzZXMgV29ybGR3aWRlIC0gJywgbWF4LmRhdGUudHh0KSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygncmVkJywgJ2dyZWVuJywgJ2JsYWNrJykpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9NyksDQogICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5rZXkuc2l6ZT11bml0KDAuMiwgJ2NtJyksDQogICAgICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTYpLA0KICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NyksDQogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90X3dvcmxkLmxpbmUgPC0gd29ybGQgJT4lDQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWNvdW50KSkgKw0KICBnZW9tX2xpbmUoYWVzKGNvbG9yPXR5cGUpKSArDQogIGxhYnModGl0bGU9cGFzdGUwKCdOdW1iZXJzIG9mIENhc2VzIFdvcmxkd2lkZSAobG9nIHNjYWxlKSAtICcsIG1heC5kYXRlLnR4dCkpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCdwdXJwbGUnLCAncmVkJywgJ2dyZWVuJywgJ2JsYWNrJykpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9NyksDQogICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5rZXkuc2l6ZT11bml0KDAuMiwgJ2NtJyksDQogICAgICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTYpLA0KICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NyksDQogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPSdsb2cxMCcpDQojIyBzaG93IHR3byBwbG90cyBzaWRlIGJ5IHNpZGUNCmdyaWQuYXJyYW5nZShwbG90MSwgcGxvdDIsIG5jb2w9MikNCg0KZ2x5LnBsb3Rfd29ybGQuYXJlYSA8LSBnZ3Bsb3RseShwbG90X3dvcmxkLmFyZWEpDQpnbHkucGxvdF93b3JsZC5hcmVhDQoNCmdseS5wbG90X3dvcmxkLmxpbmUgPC0gZ2dwbG90bHkocGxvdF93b3JsZC5saW5lKQ0KZ2x5LnBsb3Rfd29ybGQubGluZQ0KYGBgDQoNCmBgYHtyfQ0KZGF0YS53b3JsZC5hbGwgPC0gZGF0YSAlPiUgZmlsdGVyKGNvdW50cnkgPT0gIldvcmxkIikNCiMjIGEgc2NhdHRlciBwbG90IHdpdGggYSBzbW9vdGhlZCBsaW5lIGFuZCB2ZXJ0aWNhbCB4LWF4aXMgbGFiZWxzDQpwbG90X2FjYy5kZWF0aHMgPC0gZ2dwbG90KGRhdGEud29ybGQuYWxsLCBhZXMoeD1kYXRlLCB5PWRlYXRocykpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J0FjY3VtdWxhdGl2ZSBEZWF0aHMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90X3JlY292ZXJlZCA8LSBnZ3Bsb3QoZGF0YS53b3JsZC5hbGwsIGFlcyh4PWRhdGUsIHk9cmVjb3ZlcmVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nQWNjdW11bGF0aXZlIFJlY292ZXJlZCBDYXNlcycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCnBsb3RfbmV3LmRlYXRocyA8LSBnZ3Bsb3QoZGF0YS53b3JsZC5hbGwsIGFlcyh4PWRhdGUsIHk9bmV3LmRlYXRocykpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J05ldyBEZWF0aHMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90X25ldy5yZWNvdmVyZWQgPC0gZ2dwbG90KGRhdGEud29ybGQuYWxsLCBhZXMoeD1kYXRlLCB5PW5ldy5yZWNvdmVyZWQpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJ0NvdW50JykgKyBsYWJzKHRpdGxlPSdOZXcgUmVjb3ZlcmVkIENhc2VzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KIyMgc2hvdyBmb3VyIHBsb3RzIHRvZ2V0aGVyLCB3aXRoIDIgcGxvdHMgaW4gZWFjaCByb3cNCmdyaWQuYXJyYW5nZShwbG90X2FjYy5kZWF0aHMsIHBsb3RfcmVjb3ZlcmVkLCBwbG90X25ldy5kZWF0aHMsIHBsb3RfbmV3LnJlY292ZXJlZCwgbnJvdz0yKQ0KDQpgYGANCg0KYGBge3J9DQojIyBjb252ZXJ0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdCwgZm9yIGRyYXdpbmcgYXJlYSBwbG90cw0KcmF0ZXMubG9uZyA8LSBkYXRhICU+JQ0KICBzZWxlY3QoYyhjb3VudHJ5LCBkYXRlLCByYXRlLnVwcGVyLCByYXRlLmxvd2VyLCByYXRlLmRhaWx5KSkgJT4lDQogIGdhdGhlcihrZXk9dHlwZSwgdmFsdWU9Y291bnQsIC1jKGNvdW50cnksIGRhdGUpKQ0KIyBzZXQgZmFjdG9yIGxldmVscyB0byBzaG93IHRoZW0gaW4gYSBkZXNpcmFibGUgb3JkZXINCnJhdGVzLmxvbmcgJTw+JSBtdXRhdGUodHlwZT1yZWNvZGVfZmFjdG9yKHR5cGUsIHJhdGUuZGFpbHk9J0RhaWx5JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmF0ZS51cHBlcj0nVXBwZXIgYm91bmQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYXRlLmxvd2VyID0gJ0xvd2VyIGJvdW5kJykpIA0KI1ZpZXcocmF0ZXMubG9uZykgDQoNCnBsb3RfcmF0ZXMgPC0gcmF0ZXMubG9uZyAlPiUgZmlsdGVyKGNvdW50cnkgPT0gIldvcmxkIikgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCB5ID0gY291bnQsIGNvbG9yID0gdHlwZSkpICsgDQogIGdlb21fbGluZSgpICsNCiAgbGFicyh0aXRsZSA9ICJXb3JsZCBEZWF0aCBSYXRlICglKSIpICsNCiAgeGxhYigiIikgKyB5bGFiKCJEZWF0aCBSYXRlICglKSIpDQoNCnBsb3RfcmF0ZXMNCg0KZ2x5LnJhdGVzIDwtIGdncGxvdGx5KHBsb3RfcmF0ZXMpDQpnbHkucmF0ZXMNCmBgYA0KDQpgYGB7cn0NCiMjIHJhbmtpbmcgYnkgY29uZmlybWVkIGNhc2VzDQpkYXRhLmxhdGVzdC5hbGwgPC0gZGF0YSAlPiUgZmlsdGVyKGRhdGUgPT0gbWF4KGRhdGUpKSAlPiUNCiAgc2VsZWN0KGNvdW50cnksIGRhdGUsY29uZmlybWVkLCBuZXcuY29uZmlybWVkLCBjdXJyZW50LmNvbmZpcm1lZCwNCiAgICAgICAgIHJlY292ZXJlZCwgZGVhdGhzLCBuZXcuZGVhdGhzLCBkZWF0aC5yYXRlPXJhdGUubG93ZXIpICU+JQ0KICBtdXRhdGUocmFua2luZyA9IGRlbnNlX3JhbmsoZGVzYyhjb25maXJtZWQpKSkgJT4lDQogIGFycmFuZ2UocmFua2luZykNClZpZXcoZGF0YS5sYXRlc3QuYWxsKQ0KDQprIDwtIDIwDQojIyB0b3AgMjAgY291bnRyaWVzOiAyMSBpbmNsLiAnV29ybGQnDQp0b3AuY291bnRyaWVzIDwtIGRhdGEubGF0ZXN0LmFsbCAlPiUgZmlsdGVyKHJhbmtpbmcgPD0gayArIDEpICU+JQ0KICBhcnJhbmdlKHJhbmtpbmcpICU+JSBwdWxsKGNvdW50cnkpICU+JSBhcy5jaGFyYWN0ZXIoKQ0KdG9wLmNvdW50cmllcyAlPiUgc2V0ZGlmZignV29ybGQnKSAlPiUgcHJpbnQoKQ0KYGBgDQoNCmBgYHtyfQ0KZGF0YS5sYXRlc3QgPC0gZGF0YS5sYXRlc3QuYWxsICU+JSBmaWx0ZXIoIWlzLm5hKGNvdW50cnkpKSAlPiUNCiAgbXV0YXRlKGNvdW50cnk9aWZlbHNlKHJhbmtpbmcgPD0gayArIDEsIGFzLmNoYXJhY3Rlcihjb3VudHJ5KSwgJ090aGVycycpKSAlPiUNCiAgbXV0YXRlKGNvdW50cnk9Y291bnRyeSAlPiUgZmFjdG9yKGxldmVscz1jKHRvcC5jb3VudHJpZXMsICdPdGhlcnMnKSkpDQpkYXRhLmxhdGVzdCAlPD4lIGdyb3VwX2J5KGNvdW50cnkpICU+JQ0KICBzdW1tYXJpc2UoY29uZmlybWVkPXN1bShjb25maXJtZWQpLCBuZXcuY29uZmlybWVkPXN1bShuZXcuY29uZmlybWVkKSwNCiAgICAgICAgICAgIGN1cnJlbnQuY29uZmlybWVkPXN1bShjdXJyZW50LmNvbmZpcm1lZCksDQogICAgICAgICAgICByZWNvdmVyZWQ9c3VtKHJlY292ZXJlZCksIGRlYXRocz1zdW0oZGVhdGhzKSwgbmV3LmRlYXRocz1zdW0obmV3LmRlYXRocykpICU+JQ0KICBtdXRhdGUoZGVhdGgucmF0ZT0oMTAwICogZGVhdGhzL2NvbmZpcm1lZCkgJT4lIHJvdW5kKDEpKSANCmRhdGEubGF0ZXN0DQpkYXRhLmxhdGVzdCAlPD4lIHNlbGVjdChjKGNvdW50cnksIGNvbmZpcm1lZCwgZGVhdGhzLCBkZWF0aC5yYXRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcuY29uZmlybWVkLCBuZXcuZGVhdGhzLCBjdXJyZW50LmNvbmZpcm1lZCxyZWNvdmVyZWQpKSAlPiUNCiAgbXV0YXRlKHJlY292ZXIucmF0ZT0oMTAwICogcmVjb3ZlcmVkL2NvbmZpcm1lZCkgJT4lIHJvdW5kKDEpKQ0KZGF0YS5sYXRlc3QNCg0KZGZfcG9wIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIHBvcHVsYXRpb24gIikpDQpkZl9wb3AgPC0gYXMuZGF0YS5mcmFtZShkZl9wb3ApDQpkZl9wb3AgPC0gcmVuYW1lKGRmX3BvcCwiY291bnRyeSI9IkNvdW50cnkiKQ0KDQojIEFkZCBXb3JsZCBQb3B1bGF0aW9uDQp3b3JsZF9wb3AgPC0gc3VtKGRmX3BvcCRgUG9wdWxhdGlvbiAoMjAyMClgKQ0KZGZfcG9wW25yb3coZGZfcG9wKSArIDEsXSA9IGMoIldvcmxkIiwgd29ybGRfcG9wKQ0KDQojIEFkZCBPdGhlciBDb3VudHJpZXMgUG9wdWxhdGlvbg0KdG9wX3BvcCA8LSBmaWx0ZXIoZGZfcG9wLCBkZl9wb3AkY291bnRyeSAlaW4lIHRvcC5jb3VudHJpZXMgJiBkZl9wb3AkY291bnRyeSAhPSAiV29ybGQiKQ0KdG9wX3BvcCA8LSBzdW0odG9wX3BvcCRgUG9wdWxhdGlvbiAoMjAyMClgICU+JSBhcy5udW1lcmljKCkpDQpvdGhlcnNfcG9wIDwtICh3b3JsZF9wb3AgLSB0b3BfcG9wKSANCmRmX3BvcFtucm93KGRmX3BvcCkgKyAxLF0gPSBjKCJPdGhlcnMiLCBvdGhlcnNfcG9wKQ0KVmlldyhkZl9wb3ApDQoNCmRhdGEubGF0ZXN0IDwtIG1lcmdlKHggPSBkYXRhLmxhdGVzdCwgeSA9IGRmX3BvcCwgYnkgPSAiY291bnRyeSIsIGFsbC54ID0gVFJVRSkgDQpkYXRhLmxhdGVzdA0KZGF0YS5sYXRlc3QgPC0gcmVuYW1lKGRhdGEubGF0ZXN0LCJwb3B1bGF0aW9uIiA9ICJQb3B1bGF0aW9uICgyMDIwKSIpDQpkYXRhLmxhdGVzdCRwb3B1bGF0aW9uIDwtIGRhdGEubGF0ZXN0JHBvcHVsYXRpb24gJT4lIGFzLm51bWVyaWMoKQ0KZGF0YS5sYXRlc3QgIDwtIGRhdGEubGF0ZXN0ICU+JQ0KICBzZWxlY3QoYyhjb3VudHJ5LCBjb25maXJtZWQsIGRlYXRocywgZGVhdGgucmF0ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3LmNvbmZpcm1lZCwgbmV3LmRlYXRocywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY3VycmVudC5jb25maXJtZWQsIHJlY292ZXJlZCwgcmVjb3Zlci5yYXRlLCBwb3B1bGF0aW9uKSkgJT4lDQogIG11dGF0ZShjb25maXJtLnJhdGUgPSAoMTAwICogY29uZmlybWVkIC8gcG9wdWxhdGlvbikgJT4lIHJvdW5kKDEpKQ0KZGF0YS5sYXRlc3QNCmBgYA0KDQpgYGB7cn0NCiMjICUgb2YgZGVhdGgNCmRhdGEubGF0ZXN0ICU+JSBtdXRhdGUoZGVhdGgucmF0ZT1kZWF0aC5yYXRlICU+JSBmb3JtYXQobnNtYWxsPTEpICU+JSBwYXN0ZTAoJyUnKSkNCg0KIyMgY29udmVydCBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQsIGZvciBkcmF3aW5nIGFyZWEgcGxvdHMNCmRhdGEubGF0ZXN0LmxvbmcgPC0gZGF0YS5sYXRlc3QgJT4lIGZpbHRlcihjb3VudHJ5IT0nV29ybGQnKSAlPiUNCiAgZ2F0aGVyKGtleT10eXBlLCB2YWx1ZT1jb3VudCwgLWNvdW50cnkpDQojIyBzZXQgZmFjdG9yIGxldmVscyB0byBzaG93IHRoZW0gd2l0aCBwcm9wZXIgdGV4dCBhbmQgaW4gYSBkZXNpcmFibGUgb3JkZXINCmRhdGEubGF0ZXN0LmxvbmcgJTw+JSBtdXRhdGUodHlwZT1yZWNvZGVfZmFjdG9yKHR5cGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25maXJtZWQ9J1RvdGFsIENvbmZpcm1lZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWF0aHM9J1RvdGFsIERlYXRocycsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWF0aC5yYXRlPSdEZWF0aCBSYXRlICglKScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcuY29uZmlybWVkPSdOZXcgQ29uZmlybWVkIChjb21wYXJlZCB3aXRoIG9uZSBkYXkgYmVmb3JlKScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcuZGVhdGhzPSdOZXcgRGVhdGhzIChjb21wYXJlZCB3aXRoIG9uZSBkYXkgYmVmb3JlKScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJyZW50LmNvbmZpcm1lZD0nQ3VycmVudCBDb25maXJtZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjb3Zlci5yYXRlID0gJ1JlY292ZXJlZCBSYXRlKCUpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpcm0ucmF0ZSA9ICdDb25maXJtZWQgUmF0ZSglKScpKQ0KI1ZpZXcoZGF0YS5sYXRlc3QubG9uZykNCmRhdGEub25lLmRlbSA8LSBmaWx0ZXIoZGF0YS5sYXRlc3QubG9uZyx0eXBlPT0nVG90YWwgQ29uZmlybWVkJw0KICAgICAgICAgICAgICAgICAgICAgICB8IHR5cGU9PSdUb3RhbCBEZWF0aHMnDQogICAgICAgICAgICAgICAgICAgICAgIHwgdHlwZT09J0N1cnJlbnQgQ29uZmlybWVkJykNCmRhdGEudHdvLmRlbSA8LSBmaWx0ZXIoZGF0YS5sYXRlc3QubG9uZyx0eXBlPT0nRGVhdGggUmF0ZSAoJSknDQogICAgICAgICAgICAgICAgICAgICAjICB8IHR5cGU9PSdOZXcgQ29uZmlybWVkIChjb21wYXJlZCB3aXRoIG9uZSBkYXkgYmVmb3JlKScNCiAgICAgICAgICAgICAgICAgICAgIyAgIHwgdHlwZT09J05ldyBEZWF0aHMgKGNvbXBhcmVkIHdpdGggb25lIGRheSBiZWZvcmUpJw0KICAgICAgICAgICAgICAgICAgICAgICB8IHR5cGU9PSdSZWNvdmVyZWQgUmF0ZSglKScNCiAgICAgICAgICAgICAgICAgICAgICAgfCB0eXBlPT0nQ29uZmlybWVkIFJhdGUoJSknKQ0KZGF0YS50d28uZGVtDQpgYGANCg0KYGBge3J9DQojIyBiYXIgY2hhcnQNCmRhdGEub25lLmRlbSAlPiUgZ2dwbG90KGFlcyh4PWNvdW50cnksIHk9Y291bnQsIGZpbGw9Y291bnRyeSwgZ3JvdXA9Y291bnRyeSkpICsNCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknKSArDQogIGdlb21fdGV4dChhZXMobGFiZWw9Y291bnQsIHk9Y291bnQpLCBzaXplPTMsIHZqdXN0PTApICsNCiAgeGxhYignJykgKyB5bGFiKCcnKSArDQogIGxhYnModGl0bGU9cGFzdGUwKCdUb3AgMjAgQ291bnRyaWVzIHdpdGggTW9zdCBDb25maXJtZWQgQ2FzZXMgLSAnLCBtYXguZGF0ZS50eHQpKSArDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZT0nQ291bnRyeScsIGxhYmVscz1hZXMoY291bnQpKSArDQogIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScsDQogICAgICAgIHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTMpLA0KICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksDQogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpICsNCiAgZmFjZXRfd3JhcCh+dHlwZSwgbmNvbD0xLCBzY2FsZXM9J2ZyZWVfeScpDQpgYGANCg0KYGBge3J9DQpkYXRhLnR3by5kZW0kZmFjZXQgPC0gZmFjdG9yKGRhdGEudHdvLmRlbSR0eXBlLCBsZXZlbHMgPSBjKCdDb25maXJtZWQgUmF0ZSglKScsICdSZWNvdmVyZWQgUmF0ZSglKScsJ0RlYXRoIFJhdGUgKCUpJykpDQpkYXRhLnR3by5kZW0gJT4lIA0KICBnZ3Bsb3QoYWVzKHg9Y291bnRyeSwgeT1jb3VudCwgZmlsbD1jb3VudHJ5LCBncm91cD1jb3VudHJ5KSkgKw0KICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1jb3VudCwgeT1jb3VudCksIHNpemU9NCwgdmp1c3Q9MCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJycpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ1RvcCAyMCBDb3VudHJpZXMgd2l0aCBNb3N0IENvbmZpcm1lZCBDYXNlcyAtICcsIG1heC5kYXRlLnR4dCkpICsNCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lPSdDb3VudHJ5JywgbGFiZWxzPWFlcyhjb3VudCkpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSdub25lJywNCiAgICAgICAgcGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMyksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMCksDQogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTEwLGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBmYWNldF93cmFwKH5mYWNldCwgbmNvbD0xLCBzY2FsZXM9J2ZyZWVfeScpDQpgYGANCg0KYGBge3J9DQojI0dEUA0KZGZfZ2RwMjAxOSA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBnZHAxOSIpKQ0KZGZfZ2RwMjAxOSA8LSBhcy5kYXRhLmZyYW1lKGRmX2dkcDIwMTkpDQpkZl9nZHAyMDE5DQpgYGANCg0KYGBge3J9DQojaGVhbHRocmFua2luZyBkYXRhDQpkZl9oZWFsdCA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBoZWFsdGhyYW5raW5nIikpDQpkZl9oZWFsdCA8LSBhcy5kYXRhLmZyYW1lKGRmX2hlYWx0KQ0KZGZfaGVhbHQgPC0gc2VsZWN0KGRmX2hlYWx0LGMoImNvdW50cnkiLCJoZWFsdGhDYXJlSW5kZXgiKSkNClZpZXcoZGZfaGVhbHQpDQpgYGANCg0KYGBge3J9DQojVG9wMjBQb3JuaHViIGRhdGENCmRmX3Bvcm5odWIgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gUG9ybmh1YiIpKQ0KZGZfcG9ybmh1YiA8LSBhcy5kYXRhLmZyYW1lKGRmX3Bvcm5odWIpDQpkZl9wb3JuaHViDQpgYGANCg0KYGBge3J9DQojIHRlbXAgZGF0YQ0KZGZfdGVtcCA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSB3b3JsZF90ZW1wIikpDQpkZl90ZW1wIDwtIGFzLmRhdGEuZnJhbWUoZGZfdGVtcCkgDQpkZl90ZW1wJENvdW50cnlbZGZfdGVtcCRDb3VudHJ5ID09ICJVbml0ZWQgU3RhdGVzIl0gPC0gIlVTIg0KDQpkZl9jaXR5IDwtIHNlbGVjdChkZl90ZW1wLGMoIkNvdW50cnkiLCJDaXR5IikpICU+JQ0KICByZW5hbWUoY291bnRyeT1Db3VudHJ5KSAlPiUgDQogIHJlbmFtZShjaXR5PUNpdHkpDQoNCm51bW9mY2l0eSA8LSBhZ2dyZWdhdGUoY2l0eSB+IGNvdW50cnksIGRhdGEgPSBkZl9jaXR5LCBsZW5ndGgpDQoNCiNjbGVhbiBkYXRhDQpkZl90ZW1wIDwtIHNlbGVjdChkZl90ZW1wLGMoIkNvdW50cnkiLCJBdmdfWWVhciIpKSAlPiUNCiAgcmVuYW1lKGNvdW50cnk9Q291bnRyeSkNClZpZXcoZGZfdGVtcCkNCg0KI2RmX3RlbXAgPC0gZGF0YS5mcmFtZShjb3VudHJ5PWRmX3RlbXBbLDFdLGF2Zz1yb3dNZWFucyhkZl90ZW1wWywtMV0pKQ0KZGZfdGVtcCA8LSBkZl90ZW1wICU8PiUgZ3JvdXBfYnkoY291bnRyeSkgJT4lIHN1bW1hcmlzZShhdmdfdGVtcCA9IG1lYW4oQXZnX1llYXIsbmEucm0gPSBUUlVFKSU+JSByb3VuZCgxKSkNCmRmX3RlbXANCmBgYA0KDQpgYGB7cn0NCiMjdGVtcCBiYXINCmRmX3RlbXAuYWxsIDwtIGRmX3RlbXAgJT4lIG1lcmdlKGRhdGEubGF0ZXN0LmFsbCkNClZpZXcoZGZfdGVtcC5hbGwpDQpkZl90ZW1wX3RvcC5hbGwgPC0gZGZfdGVtcC5hbGwgJT4lIGZpbHRlcihjb3VudHJ5ICVpbiUgdG9wLmNvdW50cmllcykgJT4lDQogIG11dGF0ZShyYW5raW5nID0gcmFua2luZyAtIDEpICU+JQ0KICBhcnJhbmdlKHJhbmtpbmcpDQpWaWV3KGRmX3RlbXBfdG9wLmFsbCkNCmdfdGVtcF90b3AgPC0gZGZfdGVtcF90b3AuYWxsICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKGNvdW50cnksIHJhbmtpbmcpLCB5ID0gYXZnX3RlbXAsIGZpbGwgPSBhdmdfdGVtcCkpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoIlRlbXBlcmF0dXJlIGluIFRvcCAgMjAgY291bnRyaWVzIiksIHN1YnRpdGxlID0gIkF2ZXJhZ2UgVGVtcGVyYXR1cmUgaW4gVG9wIDIwIGNvdW50cmllcyB3aXRoIG1vc3QgY29uZmlybWVkIGNhc2VzICjCsEMpICgyMDIwKSIpICsNCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93ID0gIiM5M0RCRkYiLCBoaWdoID0gIiNGRjc3NzEiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWw9YXZnX3RlbXAsIHk9YXZnX3RlbXApLCBzaXplPTQsIHZqdXN0PS0wLjUpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKw0KICB0aGVtZSgNCiAgICAgICAgbGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSdub25lJywNCiAgICAgICAgcGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZSA9IDE1LCBoanVzdCA9IDAuNSksDQogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBoanVzdCA9IDAuNSksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemUgPSA5LCBhbmdsZT00NSwgaGp1c3Q9MSkpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShuYW1lID0gIkNvdW50cnkiKSArDQogIHNjYWxlX3lfZGlzY3JldGUobmFtZSA9ICJBdmVyYWdlIFRlbXBlcmF0dXJlIikNCg0KICAgICAgICAgICAjbGFicyh0aXRsZSA9ICJUZW1wZXJhdHVyZSBpbiBUb3AgIDIwIGNvdW50cmllcyIsIHN1YnRpdGxlID0gIlRlbXBlcmF0dXJlIGluIFRvcCAyMCBjb3VudHJpZXMgd2l0aCBtb3N0IGNvbmZpcm1lZCBjYXNlcyAowrBDKSIpDQogICAgICAgICANCiNnX3RlbXBfdG9wICAgICAgICAgDQpnX3RlbXBfdG9wIA0KYGBgDQoNCmBgYHtyfQ0KI2RhdGEubGF0ZXN0LmFsbA0KbGF0LmxvbmcgPC0gcmVuYW1lKGRmX2NvbmYsICJjb3VudHJ5IiA9ICJDb3VudHJ5LlJlZ2lvbiIsICJjaXR5IiA9ICJQcm92aW5jZS5TdGF0ZSIpICU+JSANCiAgc2VsZWN0KCJjb3VudHJ5IiwgIkxhdCIsICJMb25nIikgJT4lIA0KICBtZXJnZShkZl90ZW1wLmFsbFtjKCJjb3VudHJ5IiwiY29uZmlybWVkIiwgInJlY292ZXJlZCIsICJkZWF0aHMiLCAiYXZnX3RlbXAiLCAicmFua2luZyIpXSwgYnkgPSAiY291bnRyeSIpICU+JQ0KICBkaXN0aW5jdChjb3VudHJ5LCAua2VlcF9hbGwgPSBUUlVFKSAlPiUNCiAgbXV0YXRlKHJhbmtpbmcgPSByYW5raW5nIC0gMSkgJT4lDQogIGFycmFuZ2UocmFua2luZykNClZpZXcobGF0LmxvbmcpDQpgYGANCg0KYGBge3J9DQpsYWJlbF93b3JsZCA8LSBsYXQubG9uZyANCmxhYmVsX3dvcmxkJGF2Z190ZW1wIDwtIGFzLm51bWVyaWMobGFiZWxfd29ybGRbLCBuYW1lcyhsYWJlbF93b3JsZCkgJWluJSBjKCJhdmdfdGVtcCIpXSkNCmxhYmVsX3dvcmxkIDwtIGxhYmVsX3dvcmxkICU+JSAgDQogIG11dGF0ZSh0eHQ9cGFzdGUwKCc8Yj4nLHJhbmtpbmcsICc8L2I+JywNCiAgICAgICAgICAgICAgICAgICAgJzxici8+JywnPGI+Jyxjb3VudHJ5LCAnPC9iPicsDQogICAgICAgICAgICAgICAgICAgICc8YnIvPicsICJUZW1wZXJhdHVyZTogICIsYXZnX3RlbXAsICcgwrBDJywNCiAgICAgICAgICAgICAgICAgICAgJzxici8+JywgIkNvbmZpcm1lZDogICIsIGNvbmZpcm1lZCwgDQogICAgICAgICAgICAgICAgICAgICc8YnIvPicsICJEZWF0aHM6ICIsIGRlYXRocywNCiAgICAgICAgICAgICAgICAgICAgJzxici8+JywgIlJlY292ZXJlZDogIiwgcmVjb3ZlcmVkDQogICAgICAgICAgICAgICAgICAgICkpIA0KDQpsYWJlbF93b3JsZCR0eHQgPC0gbGFiZWxfd29ybGQkdHh0ICU+JSBsYXBwbHkoaHRtbHRvb2xzOjpIVE1MKQ0KbGFiZWxfd29ybGQgDQoNCmxhYmVsX3RvcCA8LSBsYWJlbF93b3JsZCAlPiUgZmlsdGVyKHJhbmtpbmcgPCAyMSkNCmxhYmVsX3RvcA0KYGBgDQoNCg0KYGBge3J9DQojIFRlbXBlcmF0dXJlIE1hcA0Kd3BhbCA8LSBjb2xvck51bWVyaWMoIllsT3JSZCIsIGxhYmVsX3dvcmxkJGF2Z190ZW1wLCBuID0gNCkNCg0KdG9wSWNvbiA8LSBtYWtlSWNvbigic3Rhci5wbmciLA0KICAjaWNvblVybCA9ICJodHRwczovL3N0YXRpYy52ZWN0ZWV6eS5jb20vc3lzdGVtL3Jlc291cmNlcy9wcmV2aWV3cy8wMDEvMTg5LzA2My9ub25fMngvc3Rhci1yb3VuZGVkLXBuZy5wbmciLA0KICBpY29uV2lkdGggPSAxMCwgaWNvbkhlaWdodCA9IDEwDQogICNpY29uQW5jaG9yWCA9IDIwLCBpY29uQW5jaG9yWSA9IDIwDQogIA0KKQ0KDQpsYWJlbF93b3JsZCA8LSBsYWJlbF93b3JsZCAlPiUgZmlsdGVyKHJhbmtpbmcgPiAyMCkgDQogIA0KbSA8LSBsZWFmbGV0KHdpZHRoPTEyMDAsIGhlaWdodD04MDApICU+JSBhZGRUaWxlcygpICANCm0gJTw+JSAgYWRkQ2lyY2xlTWFya2VycyhsYWJlbF93b3JsZCRMb25nLCBsYWJlbF93b3JsZCRMYXQsDQogICAgICAgICAgICAgICAgICAgICAgICAjIHJhZGl1cz0yK2xvZzIoeCRjb25maXJtZWQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgcmFkaXVzPTEwLCMqbG9nMihtLndvcmxkJGF2Zy50ZW1wKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHN0cm9rZT1GLA0KICAgICAgICAgICAgICAgICAgICAgICAgI2NvbG9yPSdyZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB3cGFsKGxhYmVsX3dvcmxkJGF2Z190ZW1wKSwgDQogICAgICAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eT0wLjUsDQogICAgICAgICAgICAgICAgICAgICAgICAjcG9wdXA9bGFiZWwudG9wJHR4dA0KICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWw9IGxhYmVsX3dvcmxkJHR4dCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gIldvcmxkIg0KICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUNCiAgDQogIGFkZENpcmNsZU1hcmtlcnMobGFiZWxfdG9wJExvbmcsIGxhYmVsX3RvcCRMYXQsDQogICAgICAgICAgICAgICAgICAgICAgICAjIHJhZGl1cz0yK2xvZzIoeCRjb25maXJtZWQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgcmFkaXVzPTEwLCMqbG9nMihtLndvcmxkJGF2Zy50ZW1wKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHN0cm9rZT1GLA0KICAgICAgICAgICAgICAgICAgICAgICAgI2NvbG9yPSdyZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB3cGFsKGxhYmVsX3RvcCRhdmdfdGVtcCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHk9MC41LA0KICAgICAgICAgICAgICAgICAgICAgICAgI3BvcHVwPWxhYmVsLnRvcCR0eHQNCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPSBsYWJlbF90b3AkdHh0LA0KICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSAiVG9wIDIwIENvdW50cmllcyINCiAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lDQogIA0KICBhZGRMYWJlbE9ubHlNYXJrZXJzKGxhYmVsX3RvcCRMb25nLCBsYWJlbF90b3AkTGF0LCBsYWJlbCA9IGxhYmVsX3RvcCRyYW5raW5nLA0KICAgICAgICAgICAgICAgICAgICAgIGxhYmVsT3B0aW9ucyA9IGxhYmVsT3B0aW9ucyhub0hpZGUgPSBUUlVFLCB0ZXh0T25seSA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAiaGVhZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvZmZzZXQgPSBjKDUsNCkpLA0KICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gIlRvcCAyMCBDb3VudHJpZXMiKSAlPiUNCiAgDQogIGFkZExlZ2VuZCgiYm90dG9tcmlnaHQiLCBwYWwgPSB3cGFsLCB2YWx1ZXMgPSBsYWJlbF93b3JsZCRhdmdfdGVtcCwgb3BhY2l0eSA9IDEsDQogICAgICAgICAgICBsYWJGb3JtYXQgPSBsYWJlbEZvcm1hdChzdWZmaXggPSAiIMKwQyIpLA0KICAgICAgICAgICAgdGl0bGUgPSAiVGVtcGVyYXR1cmUiKSAlPiUgDQogIA0KICBhZGRMYXllcnNDb250cm9sKA0KICAgICNiYXNlR3JvdXBzID0gYygiT1NNIChkZWZhdWx0KSIsICJUb25lciIsICJUb25lciBMaXRlIiksDQogICAgb3ZlcmxheUdyb3VwcyA9IGMoIlRvcCAyMCBDb3VudHJpZXMiLCAiV29ybGQiKSwNCiAgICBvcHRpb25zID0gbGF5ZXJzQ29udHJvbE9wdGlvbnMoY29sbGFwc2VkID0gRkFMU0UpDQogICkNCm0NCmBgYA0KDQpgYGB7cn0NCmdkcC50b3AyMCA8LSBkZl9nZHAyMDE5ICU+JQ0KICBzZWxlY3QoYygicmFuayIsICJjb3VudHJ5IiwgIkdEUCAobWlsbGlvbnMgb2YgVVMgZG9sbGFycykiKSkgJT4lDQogIG1lcmdlKGRhdGEubGF0ZXN0LmFsbCAlPiUgDQogICAgICAgICAgc2VsZWN0KGNvdW50cnksIHJhbmtpbmcsIGNvbmZpcm1lZCwgcmVjb3ZlcmVkLCBkZWF0aHMpICU+JSANCiAgICAgICAgICBmaWx0ZXIoY291bnRyeSAlaW4lIHRvcC5jb3VudHJpZXMgJiBjb3VudHJ5ICE9ICJXb3JsZCIpLCBieSA9ICJjb3VudHJ5IikgJT4lDQogIGFycmFuZ2UocmFua2luZykgJT4lIA0KICBtdXRhdGUocmFua2luZyA9IHJhbmtpbmcgLSAxKSANCmdkcC50b3AyMCAlPD4lIHJlbmFtZSgiR0RQIiA9ICJHRFAgKG1pbGxpb25zIG9mIFVTIGRvbGxhcnMpIikNCmdkcC50b3AyMA0KDQpnIDwtIGdncGxvdChnZHAudG9wMjAsIGFlcyh4ID0gR0RQLCB5ID0gcmVvcmRlcihjb3VudHJ5LCAtcmFua2luZykpKSArDQogIGdlb21faGlzdG9ncmFtKHN0YXQgPSAiaWRlbnRpdHkiLCBhZXMoZmlsbCA9IEdEUCkpKyANCiAgc2NhbGVfZmlsbF9ncmFkaWVudCgiR0RQIiwgbG93ID0gIiNGRjQwMzgiLCBoaWdoID0gIiM1MEU5NTIiKSArIA0KICBsYWJzKHRpdGxlPXBhc3RlMCgiR0RQICBvZiBUb3AgMjAgQ291bnRyaWVzIGluIDIwMTkgKG1pbGxpb25zIG9mIFVTIGRvbGxhcnMpIikpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1HRFAsIHggPSBHRFApLCBzaXplPTMuNSwgaGp1c3Q9LTAuMikgKw0KICB4bGFiKCJHRFAgKG1pbGxpb25zIG9mIFVTIGRvbGxhcnMpIikgKw0KICB5bGFiKCIiKSArDQogIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCkpDQpnDQoNCmdseS50b3AuZ2RwIDwtIGdncGxvdGx5KGcpDQpnbHkudG9wLmdkcA0KYGBgDQoNCmBgYHtyfQ0KIyBQb3JuaHViDQpkZl9wb3JuaHViIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIHBvcm5odWIiKSkNCmRmX3Bvcm5odWIgPC0gYXMuZGF0YS5mcmFtZShkZl9wb3JuaHViKQ0KZGZfcG9ybmh1Yg0KYGBgDQoNCg0KYGBge3J9DQojIFNhcnMNCmRmX3NhcnMgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gc2Fyc18yMDAzIikpDQpkZl9zYXJzIDwtIGFzLmRhdGEuZnJhbWUoZGZfc2FycykNCiNWaWV3KGRmX3NhcnMpDQoNCmRhdGVzLnMgPC0gZGZfc2Fyc1ssMV0lPiUgbWR5KCkNCnJhbmdlKGRhdGVzLnMpDQptaW4uZGF0ZS5zIDwtIG1pbihkYXRlcy5zKQ0KbWF4LmRhdGUucyA8LSBtYXgoZGF0ZXMucykNCm1pbi5kYXRlLnR4dC5zIDwtIG1pbi5kYXRlLnMgJT4lIGZvcm1hdCgnJWQgJWIgJVknKQ0KbWF4LmRhdGUudHh0LnMgPC0gbWF4LmRhdGUucyAlPiUgZm9ybWF0KCclZCAlYiAlWScpDQpkYXkxLnNhcnMgPC0gbWluLmRhdGUucw0KYGBgDQoNCmBgYHtyfQ0KIyBjbGVhbiBkYXRhIFNhcnMgDQpkYXRhLnNhcnMgPC0gZGZfc2FycyAlPiUgcmVuYW1lKGMoImRhdGUiID0gIkRhdGUiLCAiY29uZmlybWVkIiA9ICJDdW11bGF0aXZlX251bWJlciIgICwiZGVhdGhzIiA9ICJOdW1iZXJfZGVhdGhzIiwgInJlY292ZXJlZCIgPSAiTnVtYmVyX3JlY292ZXJlZCIpKSAlPiUNCiAgbXV0YXRlKGRhdGUgPSBkYXRlICU+JSBtZHkoKSkgJT4lDQogIGdyb3VwX2J5KGNvdW50cnksIGRhdGUpICU+JSBhcy5kYXRhLmZyYW1lKCkgDQpWaWV3KGRhdGEuc2FycykNCg0KDQojIEFkZCBXb3JsZCdzIFNhcnMgY2FzZXMgDQp3b3JsZC5zYXJzIDwtIGRhdGEuc2FycyAlPiUgZ3JvdXBfYnkoZGF0ZSkgJT4lIA0KICBzdW1tYXJpc2UoY291bnRyeT0nV29ybGQnLA0KICAgICAgICAgICAgY29uZmlybWVkID0gc3VtKGNvbmZpcm1lZCwgbmEucm09VCksDQogICAgICAgICAgICBkZWF0aHMgPSBzdW0oZGVhdGhzLCBuYS5ybT1UKSwNCiAgICAgICAgICAgIHJlY292ZXJlZCA9IHN1bShyZWNvdmVyZWQsIG5hLnJtPVQpKQ0KDQpkYXRhLnNhcnMgJTw+JSByYmluZCh3b3JsZC5zYXJzKQ0KZGF0YS5zYXJzICU8PiUgbXV0YXRlKGN1cnJlbnQuY29uZmlybWVkID0gY29uZmlybWVkIC0gZGVhdGhzIC0gcmVjb3ZlcmVkKQ0KI1ZpZXcod29ybGQuc2FycykgDQojVmlldyhkYXRhLnNhcnMpDQpgYGANCg0KDQpgYGB7cn0NCiNyYXRlDQpkYXRhLnNhcnMgJTw+JSBhcnJhbmdlKGNvdW50cnksIGRhdGUpDQpuIDwtIG5yb3coZGF0YS5zYXJzKQ0KZGF5MS5zYXJzIDwtIG1pbihkYXRhLnNhcnMkZGF0ZSkNCmRhdGEuc2FycyAlPD4lIG11dGF0ZShuZXcuY29uZmlybWVkID0gaWZlbHNlKGRhdGUgPT0gZGF5MS5zYXJzLCBOQSwgY29uZmlybWVkIC0gbGFnKGNvbmZpcm1lZCwgbj0xKSksDQogICAgICAgICAgICAgICAgIG5ldy5kZWF0aHMgPSBpZmVsc2UoZGF0ZSA9PSBkYXkxLnNhcnMsIE5BLCBkZWF0aHMgLSBsYWcoZGVhdGhzLCBuPTEpKSwNCiAgICAgICAgICAgICAgICAgbmV3LnJlY292ZXJlZCA9IGlmZWxzZShkYXRlID09IGRheTEuc2FycywgTkEsIHJlY292ZXJlZCAtIGxhZyhyZWNvdmVyZWQsIG49MSkpKQ0KZGF0YS5zYXJzICU8PiUgbXV0YXRlKG5ldy5jb25maXJtZWQgPSBpZmVsc2UobmV3LmNvbmZpcm1lZCA8IDAsIDAsIG5ldy5jb25maXJtZWQpLA0KICAgICAgICAgICAgICAgICBuZXcuZGVhdGhzID0gaWZlbHNlKG5ldy5kZWF0aHMgPCAwLCAwLCBuZXcuZGVhdGhzKSwNCiAgICAgICAgICAgICAgICAgbmV3LnJlY292ZXJlZCA9IGlmZWxzZShuZXcucmVjb3ZlcmVkIDwgMCwgMCwgbmV3LnJlY292ZXJlZCkpDQojIyBkZWF0aCByYXRlIGJhc2VkIG9uIHRvdGFsIGRlYXRocyBhbmQgcmVjb3ZlcmVkIGNhc2VzDQpkYXRhLnNhcnMgJTw+JSBtdXRhdGUocmF0ZS51cHBlciA9ICgxMDAgKiBkZWF0aHMgLyAoZGVhdGhzICsgcmVjb3ZlcmVkKSkgJT4lIHJvdW5kKDEpLA0KICAgICAgICAgICAgICAgICByYXRlLnVwcGVyID0gaWZlbHNlKGlzLm5hbihyYXRlLnVwcGVyKSwgMCwgcmF0ZS51cHBlcikpDQoNCiMjIGxvd2VyIGJvdW5kOiBkZWF0aCByYXRlIGJhc2VkIG9uIHRvdGFsIGNvbmZpcm1lZCBjYXNlcw0KZGF0YS5zYXJzICU8PiUgbXV0YXRlKHJhdGUubG93ZXIgPSAoMTAwICogZGVhdGhzIC8gY29uZmlybWVkKSAlPiUgcm91bmQoMSksDQogICAgICAgICAgICAgICAgIHJhdGUubG93ZXIgPSBpZmVsc2UoaXMubmFuKHJhdGUubG93ZXIpLCAwLCByYXRlLmxvd2VyKSkNCg0KIyMgZGVhdGggcmF0ZSBiYXNlZCBvbiB0aGUgbnVtYmVyIG9mIGRlYXRoL3JlY292ZXJlZCBvbiBldmVyeSBzaW5nbGUgZGF5DQpkYXRhLnNhcnMgJTw+JSBtdXRhdGUocmF0ZS5kYWlseSA9ICgxMDAgKiBuZXcuZGVhdGhzIC8gKG5ldy5kZWF0aHMgKyBuZXcucmVjb3ZlcmVkKSkgJT4lIHJvdW5kKDEpLA0KICAgICAgICAgICAgICAgICByYXRlLmRhaWx5ID0gaWZlbHNlKGlzLm5hbihyYXRlLmRhaWx5KSwgMCwgcmF0ZS5kYWlseSkpDQoNClZpZXcoZGF0YS5zYXJzKQ0KYGBgDQoNCmBgYHtyfQ0KIyMgY29udmVydCBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQNCmRhdGEuc2Fycy5sb25nIDwtIGRhdGEuc2FycyAlPiUNCiAgc2VsZWN0KGMoY291bnRyeSwgZGF0ZSwgY29uZmlybWVkLCBjdXJyZW50LmNvbmZpcm1lZCwgcmVjb3ZlcmVkLCBkZWF0aHMpKSAlPiUNCiAgZ2F0aGVyKGtleT10eXBlLCB2YWx1ZT1jb3VudCwgLWMoY291bnRyeSwgZGF0ZSkpDQojIyBzZXQgZmFjdG9yIGxldmVscyB0byBzaG93IHRoZW0gaW4gYSBkZXNpcmFibGUgb3JkZXINCmRhdGEuc2Fycy5sb25nICU8PiUgbXV0YXRlKHR5cGU9cmVjb2RlX2ZhY3Rvcih0eXBlLCBjb25maXJtZWQ9J1RvdGFsIENvbmZpcm1lZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnQuY29uZmlybWVkPSdDdXJyZW50IENvbmZpcm1lZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlY292ZXJlZD0nUmVjb3ZlcmVkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVhdGhzPSdEZWF0aHMnKSkNClZpZXcoZGF0YS5zYXJzLmxvbmcpDQoNCiMgV29ybGQgc2FycycgbG9uZyBkYXRhIA0Kd29ybGQuc2Fycy5sb25nIDwtIGRhdGEuc2Fycy5sb25nICU+JQ0KICBmaWx0ZXIoY291bnRyeSA9PSAiV29ybGQiKQ0KVmlldyh3b3JsZC5zYXJzLmxvbmcpDQoNCmcgPC0gZ2dwbG90KHdvcmxkLnNhcnMubG9uZywgYWVzKGRhdGUsIGNvdW50LCBjb2xvciA9IHR5cGUpKSArDQogIGdlb21fbGluZSgpKw0KICBsYWJzKHRpdGxlID0gIk51bWJlciBvZiBDYXNlcyBXb3JsZHdpZGU6IFNBUnMiKSsNCiAgeGxhYigiIikrDQogIHlsYWIoIiIpDQpnDQoNCmdseS5nIDwtIGdncGxvdGx5KGcpDQpnbHkuZw0KZ2x5LnBsb3QyDQpgYGANCg0KYGBge3J9DQpkZl9zYXJzX2xhc3RkYXRlIDwtIGRhdGEuc2FycyAlPiUNCiAgZmlsdGVyKGRhdGUgPT0gbWF4LmRhdGUucykNCg0KZGZfc2Fyc19sYXN0ZGF0ZQ0KYGBgDQoNCg0KYGBge3J9DQojIyBDdXJyZW50IENvbmZpcm1lZCBDYXNlcw0KZGF0YS5zYXJzLndvcmxkIDwtIGRhdGEuc2FycyAlPiUgZmlsdGVyKGNvdW50cnk9PSdXb3JsZCcpDQojVmlldyhkYXRhLnNhcnMud29ybGQpDQpuIDwtIG5yb3coZGF0YS5zYXJzLndvcmxkKQ0KVmlldyhkYXRhLnNhcnMud29ybGQpDQpwbG90X3NhcnMuY3VycmNvbmYgPC0gZ2dwbG90KGRhdGEuc2Fycy53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1jdXJyZW50LmNvbmZpcm1lZCkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J0N1cnJlbnQgQ29uZmlybWVkIENhc2VzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KcGxvdF9zYXJzLm5ld2NvbmYgPC0gZ2dwbG90KGRhdGEuc2Fycy53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1uZXcuY29uZmlybWVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nRGFpbHkgTmV3IENvbmZpcm1lZCBDYXNlcycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCiMjIHNob3cgdHdvIHBsb3RzIHNpZGUgYnkgc2lkZQ0KZ3JpZC5hcnJhbmdlKHBsb3Rfc2Fycy5jdXJyY29uZiwgcGxvdF9zYXJzLm5ld2NvbmYsIG5jb2w9MikNCmBgYA0KDQpgYGB7cn0NCiMjIGEgc2NhdHRlciBwbG90IHdpdGggYSBzbW9vdGhlZCBsaW5lIGFuZCB2ZXJ0aWNhbCB4LWF4aXMgbGFiZWxzDQpwbG90X3NhcnMuYWNjZGVhdGhzIDwtIGdncGxvdChkYXRhLnNhcnMud29ybGQsIGFlcyh4PWRhdGUsIHk9ZGVhdGhzKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nQWNjdW11bGF0aXZlIERlYXRocycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCnBsb3Rfc2Fycy5hY2NyZWNvdiA8LSBnZ3Bsb3QoZGF0YS5zYXJzLndvcmxkLCBhZXMoeD1kYXRlLCB5PXJlY292ZXJlZCkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J0FjY3VtdWxhdGl2ZSBSZWNvdmVyZWQgQ2FzZXMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90X3NhcnMubmV3ZGVhdGhzIDwtIGdncGxvdChkYXRhLnNhcnMud29ybGQsIGFlcyh4PWRhdGUsIHk9bmV3LmRlYXRocykpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J05ldyBEZWF0aHMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90X3NhcnMubmV3cmVjb3YgPC0gZ2dwbG90KGRhdGEuc2Fycy53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1uZXcucmVjb3ZlcmVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nTmV3IFJlY292ZXJlZCBDYXNlcycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCiMjIHNob3cgZm91ciBwbG90cyB0b2dldGhlciwgd2l0aCAyIHBsb3RzIGluIGVhY2ggcm93DQpncmlkLmFycmFuZ2UocGxvdF9zYXJzLmFjY2RlYXRocywgcGxvdF9zYXJzLmFjY3JlY292LCBwbG90X3NhcnMubmV3ZGVhdGhzLCBwbG90X3NhcnMubmV3cmVjb3YsIG5yb3c9MikNCmBgYA0KDQoNCmBgYHtyfQ0KI1RvcCAyMCB3aXRoIGdkcA0KZGF0YS5sb25nR0RQIDwtIGRmX2dkcCAlPiUgZ2F0aGVyKGtleT15ZWFyLCB2YWx1ZT1HRFAsIC1jKGNvdW50cnkpKQ0KZGF0YS50b3AgPC0gZGF0YS5sYXRlc3QgJT4lIGZpbHRlcighY291bnRyeSAlaW4lIGMoJ1dvcmxkJywgJ090aGVycycpKQ0KZGF0YS50b3AgPC0gaGVhZChkYXRhLnRvcCwyMCkNCiNWaWV3KGRhdGEubGF0ZXN0KQ0KZGF0YS5nZHAgPC0gZmlsdGVyKGRhdGEubG9uZ0dEUCx5ZWFyPT0nMjAyMCcpDQpkZl9zYXJzX2xhc3RkYXRlX2NvbmZpcm1lZCA8LSBkZl9zYXJzX2xhc3RkYXRlICU+JQ0KICBzZWxlY3QoImNvdW50cnkiLCAiY29uZmlybWVkIikgJT4lDQogIHJlbmFtZShzYXJzID0gImNvbmZpcm1lZCIpDQojVmlldyhkZl9zYXJzX2xhc3RkYXRlX2NvbmZpcm1lZCkNCiNtZXJnZQ0KbWVyZ2NvdW50cnkgPSBmdW5jdGlvbihkYXRhMSxkYXRhMil7DQogIGRhdGEgPC0gbWVyZ2UoeCA9IGRhdGExLCB5ID0gZGF0YTIsIGJ5ID0gImNvdW50cnkiLCBhbGwueCA9IFRSVUUpIA0KICByZXR1cm4oZGF0YSkNCn0NCmRhdGEudG9wLndvcmxkIDwtIG1lcmdlKHggPSBkYXRhLnRvcCwgeSA9IGRmX2dkcDIwMTksIGJ5ID0gImNvdW50cnkiLCBhbGwueCA9IFRSVUUpICU+JSANCiAgc2VsZWN0KC1jKGNvZGUscmFuayxuZXcuY29uZmlybWVkLG5ldy5kZWF0aHMsY3VycmVudC5jb25maXJtZWQscG9wdWxhdGlvbikpICU+JSANCiAgcmVuYW1lKEdEUD0iR0RQIChtaWxsaW9ucyBvZiBVUyBkb2xsYXJzKSIpDQoNCmRhdGEudG9wLndvcmxkIDwtIG1lcmdlKHggPSBkYXRhLnRvcC53b3JsZCwgeSA9IGRmX2hlYWx0LCBieSA9ICJjb3VudHJ5IiwgYWxsLnggPSBUUlVFKSAlPiUNCiAgcmVuYW1lKGhlYWx0aGNhcmU9ImhlYWx0aENhcmVJbmRleCIpDQojZGF0YS50b3Aud29ybGQgPC0gbWVyZ2NvdW50cnkoZGF0YS50b3Aud29ybGQsIGRmX3RlbXApDQoNCmRhdGEudG9wLndvcmxkIDwtIG1lcmdlKHggPSBkYXRhLnRvcC53b3JsZCwgeSA9IGRmX3Bvcm5odWIsIGJ5ID0gImNvdW50cnkiLCBhbGwueCA9IFRSVUUpICU+JQ0KICByZW5hbWUoUG9ybmh1YiA9ICJQb3JuaHViSW5kZXgoJSkiKQ0KDQpkYXRhLnRvcC53b3JsZCA8LSBtZXJnZSh4ID0gZGF0YS50b3Aud29ybGQsIHkgPSBkZl9zYXJzX2xhc3RkYXRlX2NvbmZpcm1lZCwgYnkgPSAiY291bnRyeSIsIGFsbC54ID0gVFJVRSkgDQoNCg0KZGF0YS50b3Aud29ybGQgPC0gbWVyZ2NvdW50cnkoZGF0YS50b3Aud29ybGQsIGRmX3RlbXApDQppbmRleCA8LSBpcy5uYShkYXRhLnRvcC53b3JsZCkNCmRhdGEudG9wLndvcmxkW2luZGV4XSA8LSAwDQpkYXRhLnRvcC53b3JsZA0KI1ZpZXcoZGF0YS50b3Aud29ybGQpDQoNCg0Kbm9ybWFsaXplID0gZnVuY3Rpb24oZGF0YSl7DQogICNyZXR1cm4gKChkYXRhIC0gbWluKGRhdGEsbmEucm0gPSBUUlVFKSkvKG1heChkYXRhLG5hLnJtID0gVFJVRSkgLSBtaW4oZGF0YSxuYS5ybSA9IFRSVUUpKSkNCiAgeiA8LSBzY2FsZShkYXRhKTsNCiAgdGFuaCh6LzIpDQp9DQpub3JtX2RhdGEgPSBhcy5kYXRhLmZyYW1lKGFwcGx5KGRhdGEudG9wLndvcmxkWywyOjEzXSwyLG5vcm1hbGl6ZSkpDQpjb3JyX2RhdGEgPC0gbm9ybV9kYXRhDQpub3JtX2RhdGEkY291bnRyeSA8LSBjKCJBcmdlbnRpbmEiLCJCYW5nbGFkZXNoIiwiQnJhemlsIiwiQ2hpbGUiLCJDb2xvbWJpYSIsIkZyYW5jZSIsIkdlcm1hbnkiLCJJbmRpYSIsIklyYW4iLCJJdGFseSIsIk1leGljbyIsIlBha2lzdGFuIiwiUGVydSIsIlJ1c3NpYSIsInNhdWRpIEFyYWJpYSIsIlNvdXRoIEFmcmljYSIsIlNwYWluIiwiVHVya2V5IiwiVW5pdGVkIEtpbmdkb20iLCJVUyIpDQojVmlldyhub3JtX2RhdGEpDQoNCg0Kbm9ybV9kYXRhX3Bsb3QgPC0gc2VsZWN0KG5vcm1fZGF0YSwiY291bnRyeSIsImNvbmZpcm0ucmF0ZSIsImRlYXRoLnJhdGUiLCJyZWNvdmVyLnJhdGUiLCJoZWFsdGhjYXJlIiwiUG9ybmh1YiIsIkdEUCIsImF2Z190ZW1wIiwgInNhcnMiKQ0Kbm9ybV9kYXRhX3Bsb3QgJTw+JSBnYXRoZXIoa2V5PXR5cGUsIHZhbHVlPWNvdW50LCAtYyhjb3VudHJ5KSkNCmxldmVsX29yZGVyIDwtIGZhY3Rvcihub3JtX2RhdGFfcGxvdCR0eXBlLCANCiAgICAgICAgICAgICAgICAgICAgICBsZXZlbCA9IGMoInNhcnMiLCJQb3JuaHViIiwiR0RQIiwiYXZnX3RlbXAiLCJoZWFsdGhjYXJlIiwicmVjb3Zlci5yYXRlIiwiZGVhdGgucmF0ZSIsImNvbmZpcm0ucmF0ZSIpKQ0KZ2dwbG90KGRhdGEgPSBub3JtX2RhdGFfcGxvdCwgYWVzKHg9Y291bnRyeSwgeT1sZXZlbF9vcmRlciwgZmlsbD1jb3VudCkpICsgDQogIGdlb21fdGlsZSgpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAicGluayIsIGhpZ2ggPSAiYmx1ZSIpICsNCiAgeGxhYigiIikgKw0KICB5bGFiKCIiKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLHZqdXN0ID0gMSkpKw0KICB0aGVtZSgNCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgI2xlZ2VuZC5wb3NpdGlvbiA9ICJub25lIg0KICApDQpgYGANCg0KYGBge3J9DQojY29ycmVsYXRpb24NCmNvcnJfZGF0YSAlPD4lIHNlbGVjdChjKEdEUCxjb25maXJtLnJhdGUsZGVhdGgucmF0ZSxyZWNvdmVyLnJhdGUsaGVhbHRoY2FyZSxhdmdfdGVtcCxQb3JuaHViLCBzYXJzKSkNCmhlYWQoY29ycl9kYXRhKQ0KY29yKGNvcnJfZGF0YSkNCmdnY29ycnBsb3QoY29yKGNvcnJfZGF0YSksaGMub3JkZXIgPSBUUlVFLA0KICAgICAgICAgICBvdXRsaW5lLmNvbG9yID0gIndoaXRlIiwNCiAgICAgICAgICAgY29sb3JzID0gYygiIzZEOUVDMSIsIndoaXRlIiwiI0U0NjcyNiIpLA0KICAgICAgICAgICBsYWIgPSBUUlVFKQ0KYGBgDQoNCmBgYHtyfQ0KIyMgRGF0YSBpbiBVUw0KDQpkZl91cyA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBjb3ZpZHVzIikpDQpkZl91cyA8LSBhcy5kYXRhLmZyYW1lKGRmX3VzKSANCmRmX3VzIDwtIHNlbGVjdChkZl91cywgZGF0ZSwgc3RhdGUsIGNhc2VzLCBkZWF0aHMpDQoNCmRmX3VzX3BvcCA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBkYXRhX3BvcHVsYXRpb24iKSkgDQpkZl91c19wb3AgPC0gYXMuZGF0YS5mcmFtZShkZl91c19wb3ApDQoNCmRmX3VzX2dlbmRlciA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBkYXRhX2dlbmRlciIpKSANCmRmX3VzX2dlbmRlciA8LSBhcy5kYXRhLmZyYW1lKGRmX3VzX2dlbmRlcikNCg0KZGZfdXNfZXRobmljIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGRhdGFfZXRobmljIikpIA0KZGZfdXNfZXRobmljIDwtIGFzLmRhdGEuZnJhbWUoZGZfdXNfZXRobmljKQ0KDQpkZl91c19sb2NrZG93biA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBkYXRhX2xvY2tkb3duIikpIA0KZGZfdXNfbG9ja2Rvd24gPC0gYXMuZGF0YS5mcmFtZShkZl91c19sb2NrZG93bikNCg0KZGZfdXNfaGVhbHRoIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGRhdGFfaGVhbHRoIikpIA0KZGZfdXNfaGVhbHRoIDwtIGFzLmRhdGEuZnJhbWUoZGZfdXNfaGVhbHRoKQ0KDQpkZl91c190ZXN0aW5nIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGRhdGFfdGVzdGluZyIpKSANCmRmX3VzX3Rlc3RpbmcgPC0gYXMuZGF0YS5mcmFtZShkZl91c190ZXN0aW5nKQ0KDQpgYGANCg0KYGBge3J9DQpkZl91cyA8LSBkZl91cyAgJT4lDQogIG11dGF0ZShkYXRlID0gZGF0ZSAlPiUgbWR5KCkpICU+JQ0KICByZW5hbWUoImNvbmZpcm1lZCIgPSAiY2FzZXMiKQ0KI2RhdGEudXMNCg0KZGF0ZXMudXMgPC0gZGZfdXNbLDFdDQpyYW5nZShkYXRlcy51cykNCm1pbi5kYXRlLnVzIDwtIG1pbihkYXRlcy51cykNCm1heC5kYXRlLnVzIDwtIG1heChkYXRlcy51cykNCm1pbi5kYXRlLnR4dC51cyA8LSBtaW4uZGF0ZS51cyAlPiUgZm9ybWF0KCclZCAlYiAlWScpDQptYXguZGF0ZS50eHQudXMgPC0gbWF4LmRhdGUudXMgJT4lIGZvcm1hdCgnJWQgJWIgJVknKQ0KZGF5MS51cyA8LSBtaW4oZGZfdXMkZGF0ZSkNCg0KZGF0YS51cy50b3RhbCA8LSBkZl91cyAlPiUgZ3JvdXBfYnkoZGF0ZSkgJT4lDQogIHN1bW1hcmlzZShzdGF0ZT0nVVMnLA0KICAgICAgICAgICAgY29uZmlybWVkID0gc3VtKGNvbmZpcm1lZCwgbmEucm09VCksDQogICAgICAgICAgICBkZWF0aHMgPSBzdW0oZGVhdGhzLCBuYS5ybT1UKSkNCiNWaWV3KGRhdGEudXMudG90YWwpDQoNCmRhdGEudXMgPC0gZGZfdXMNCmRhdGEudXMgJTw+JSByYmluZChkYXRhLnVzLnRvdGFsKQ0KVmlldyhkYXRhLnVzKQ0KDQpkYXRhLnVzLmxvbmcgPC0gZGF0YS51cyAlPiUgDQogIGdhdGhlcihrZXkgPSB0eXBlLCB2YWx1ZSA9IGNvdW50LCAtYyhkYXRlLCBzdGF0ZSkpIA0KI2RhdGEudXMubG9uZw0KDQp1cy50b3RhbCA8LSBkYXRhLnVzLnRvdGFsICU+JQ0KICBtdXRhdGUobmV3LmNvbmZpcm1lZCA9IGlmZWxzZShkYXRlID09IGRheTEsIDAsIGNvbmZpcm1lZCAtIGxhZyhjb25maXJtZWQsIG49MSkpLA0KICAgICAgICAgICAgICAgIG5ldy5kZWF0aHMgPSBpZmVsc2UoZGF0ZSA9PSBkYXkxLCAwLCBkZWF0aHMgLSBsYWcoZGVhdGhzLCBuPTEpKSkNCg0KdXMudG90YWwgJTw+JSBtdXRhdGUobmV3LmNvbmZpcm1lZCA9IGlmZWxzZShuZXcuY29uZmlybWVkIDwgMCwgMCwgbmV3LmNvbmZpcm1lZCksDQogICAgICAgICAgICAgICAgIG5ldy5kZWF0aHMgPSBpZmVsc2UobmV3LmRlYXRocyA8IDAsIDAsIG5ldy5kZWF0aHMpKQ0KdXMudG90YWwNCg0KdXMudG90YWwubG9uZyA8LSB1cy50b3RhbCAlPiUgDQogIGdhdGhlcihrZXkgPSB0eXBlLCB2YWx1ZSA9IGNvdW50LCAtYyhkYXRlLCBzdGF0ZSkpDQoNClZpZXcodXMudG90YWwubG9uZykNCmBgYA0KDQoNCmBgYHtyfQ0KcGxvdF91cy5jYXNlcyA8LSBkYXRhLnVzLmxvbmcgJT4lIGZpbHRlcihzdGF0ZSA9PSAiVVMiKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSwgeSA9IGNvdW50KSkgKw0KICBnZW9tX2FyZWEoYWVzKGZpbGw9dHlwZSksIGFscGhhPTAuNSkgKw0KICBsYWJzKHRpdGxlID0gcGFzdGUwKCJDdW11bGF0aXZlIGNhc2VzIGluIFVTIDogIiwgbWluLmRhdGUudHh0LnVzLCAnLScsIG1heC5kYXRlLnR4dC51cywgIiAoTG9nIFNjYWxlKSIpKSAgKw0KICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9J2xvZzEwJykrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCdyZWQnLCAnYmxhY2snKSkrDQogIHlsYWIoIiIpDQoNCnBsb3RfdXMubmV3Y29uZiA8LSB1cy50b3RhbC5sb25nICU+JQ0KICBmaWx0ZXIodHlwZSAlaW4lIGMoIm5ldy5jb25maXJtZWQiKSkgJT4lDQogIGdncGxvdChhZXMoeCA9IGRhdGUsIHkgPSBjb3VudCwgY29sb3IgPSB0eXBlKSkgKyANCiAgZ2VvbV9saW5lKCkgKyANCiAgbGFicyh0aXRsZSA9IHBhc3RlMCgiRGFpbHkgY29uZmlybWVkIGNhc2VzIGluIFVTIDogIiwgbWluLmRhdGUudHh0LnVzLCAnLScsIG1heC5kYXRlLnR4dC51cykpICsNCiAgeGxhYigiRGF0ZSIpICsNCiAgeWxhYigiQ29uZmlybWVkIGNhc2VzIikNCg0KZ2x5LnBsb3RfdXMuY2FzZXMgPC0gZ2dwbG90bHkocGxvdF91cy5jYXNlcykNCmdseS5wbG90X3VzLm5ld2NvbmYgPC0gZ2dwbG90bHkocGxvdF91cy5uZXdjb25mKQ0KDQpnbHkucGxvdF91cy5jYXNlcw0KZ2x5LnBsb3RfdXMubmV3Y29uZg0KDQpgYGANCg0KYGBge3J9DQpkYXRhLnVzICU8PiUgbXV0YXRlKG5ldy5jb25maXJtZWQgPSBpZmVsc2UoZGF0ZSA9PSBkYXkxLCBOQSwgY29uZmlybWVkIC0gbGFnKGNvbmZpcm1lZCwgbj0xKSksDQogICAgICAgICAgICAgICAgIG5ldy5kZWF0aHMgPSBpZmVsc2UoZGF0ZSA9PSBkYXkxLCBOQSwgZGVhdGhzIC0gbGFnKGRlYXRocywgbj0xKSkpDQoNCmRhdGEudXMgJTw+JSBtdXRhdGUobmV3LmNvbmZpcm1lZCA9IGlmZWxzZShuZXcuY29uZmlybWVkIDwgMCwgMCwgbmV3LmNvbmZpcm1lZCksDQogICAgICAgICAgICAgICAgIG5ldy5kZWF0aHMgPSBpZmVsc2UobmV3LmRlYXRocyA8IDAsIDAsIG5ldy5kZWF0aHMpKQ0KVmlldyhkYXRhLnVzKQ0KDQojZGF0YS51cy5kYWlseSA8LSBkYXRhLnVzICU+JSBmaWx0ZXIoc3RhdGUgPT0gIlVTIikNCiNWaWV3KGRhdGEudXMuZGFpbHkpDQoNCmRhdGEudXMucG9wIDwtIGRmX3VzX3BvcCAlPiUgc2VsZWN0KFN0YXRlLCBQb3B1bGF0aW9uKSAlPiUNCiAgcmVuYW1lKCJzdGF0ZSIgPSAiU3RhdGUiKSANCiNkYXRhLnVzLnBvcA0KDQpkYXRhLnVzLmdlbmRlciA8LSBkZl91c19nZW5kZXIgJT4lIHNlbGVjdChTdGF0ZSwgTWFsZSwgRmVtYWxlKSAlPiUNCiAgcmVuYW1lKCJzdGF0ZSIgPSAiU3RhdGUiKSANCmRhdGEudXMuZ2VuZGVyDQoNCmRhdGEudXMubGF0ZXN0IDwtIGRhdGEudXMgJT4lDQogIGZpbHRlcihkYXRlID09IG1heC5kYXRlLnVzKSAlPiUgDQogIG1lcmdlKGRhdGEudXMucG9wLCBieSA9ICJzdGF0ZSIsIGFsbC54ID0gVCkgJT4lDQogIG1lcmdlKGRhdGEudXMuZ2VuZGVyLCBieSA9ICJzdGF0ZSIsIGFsbC54ID0gVCkNCiNWaWV3KGRhdGEudXMubGF0ZXN0KQ0KDQpkYXRhLnVzLmxhdGVzdCRQb3B1bGF0aW9uW2RhdGEudXMubGF0ZXN0JHN0YXRlID09ICJVUyJdIDwtIHN1bShkYXRhLnVzLnBvcCRQb3B1bGF0aW9uKQ0KZGF0YS51cy5sYXRlc3QgJTw+JSBtdXRhdGUocmFua2luZyA9IGRlbnNlX3JhbmsoZGVzYyhjb25maXJtZWQpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpcm1lZC5yYXRlID0gKDEwMCAqIGNvbmZpcm1lZCAvIFBvcHVsYXRpb24pICU+JSByb3VuZCgyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlYXRoLnJhdGUgPSAoMTAwICogZGVhdGhzIC8gY29uZmlybWVkKSAlPiUgcm91bmQoMiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBNYWxlLmNvbmZpcm1lZCA9IChNYWxlICogY29uZmlybWVkKSAlPiUgcm91bmQoMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBGZW1hbGUuY29uZmlybWVkID0gRmVtYWxlICogY29uZmlybWVkICU+JSByb3VuZCgwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIE1hbGUuZGVhdGhzID0gTWFsZSAqIGRlYXRocywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEZlbWFsZS5kZWF0aHMgPSBGZW1hbGUgKiBkZWF0aHMpICU+JQ0KICBhcnJhbmdlKHJhbmtpbmcpIA0KDQp0b3AudXMgPC0gZGF0YS51cy5sYXRlc3RbLDFdDQp0b3AudXMNCg0KVmlldyh0b3AudXMpDQoNCmRhdGEudXMubGF0ZXN0DQoNCmBgYA0KDQpgYGB7cn0NCiMgTGlzdCBvZiB0b3AgMjAgc3RhdGUNCmsgPC0gMjANCmRhdGEudXMudG9wIDwtIGRhdGEudXMubGF0ZXN0ICU+JQ0KICBmaWx0ZXIocmFua2luZyA8PSBrKzEpICU+JSANCiAgYXJyYW5nZShyYW5raW5nKSANClZpZXcoZGF0YS51cy50b3ApDQoNCnVzLnN0YXRlLnRvcCA8LSBkYXRhLnVzLnRvcCAlPiUgcHVsbChzdGF0ZSkgJT4lIGFzLmNoYXJhY3RlcigpDQp1cy5zdGF0ZS50b3AgICU+JSBzZXRkaWZmKCdVUycpICU+JSBwcmludCgpDQoNCiMgY29uZmlybWVkIHJhdGUgJiBkZWF0aCByYXRlIG9mIHRvcCAyMCBzdGF0ZQ0KZy5yYXRlIDwtIGRhdGEudXMubGF0ZXN0ICU+JSBmaWx0ZXIoc3RhdGUgJWluJSB1cy5zdGF0ZS50b3AgJiBzdGF0ZSAhPSAiVVMiKSAlPiUNCiAgc2VsZWN0KHN0YXRlLCBjb25maXJtZWQucmF0ZSwgZGVhdGgucmF0ZSwgcmFua2luZykgJT4lDQogIGdhdGhlcihrZXkgPSBUeXBlLCB2YWx1ZSA9IFBlcmNlbnQsIC1jKHN0YXRlLCByYW5raW5nKSkgJT4lDQogIGdncGxvdChhZXMoeD1yZW9yZGVyKHN0YXRlLCAtZGVzYyhyYW5raW5nKSksIHk9UGVyY2VudCwgZmlsbCA9IFBlcmNlbnQpKSArDQogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JykgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICIjZWJiYzYyIiwgaGlnaCA9ICIjYjQyMDA2IikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPVBlcmNlbnQsIHk9UGVyY2VudCksIHNpemU9Mywgdmp1c3Q9MCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJycpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ0NvbmZpcm1lZCBSYXRlICYgRGVhdGggUmF0ZSBvZiBUb3AgMjAgU3RhdGUgaW4gVVMnKSkgKw0KICAjc2NhbGVfZmlsbF9jb250aW51b3VzKG5hbWU9J1N0YXRlJywgbGFiZWxzPWFlcyhQZXJjZW50KSkgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb249J25vbmUnLA0KICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEzKSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLA0KICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSArDQogIGZhY2V0X3dyYXAoflR5cGUsIG5jb2w9MSwgc2NhbGVzPSdmcmVlX3knKSANCg0KZy5yYXRlDQpgYGANCg0KYGBge3J9DQp1cy5jb25maXJtZWQubnVtIDwtIGRhdGEudXMudG9wJGNvbmZpcm1lZFtkYXRhLnVzLnRvcCRzdGF0ZSA9PSAiVVMiXSAlPiUgYXMubnVtZXJpYygpDQp1cy5jb25maXJtZWQubnVtDQoNCmRhdGEudXMudG9wICU8PiUgbXV0YXRlKGNvbmZpcm1lZC5wZXIudXMgPSAoY29uZmlybWVkICogMTAwIC8gdXMuY29uZmlybWVkLm51bSkgJT4lIHJvdW5kKDEpKQ0KZGF0YS51cy50b3ANCg0KZy51cy50b3AxIDwtIGRhdGEudXMudG9wICU+JQ0KICBmaWx0ZXIoc3RhdGUgIT0gIlVTIikgJT4lDQogIHNlbGVjdChzdGF0ZSwgY29uZmlybWVkLCBjb25maXJtZWQucGVyLnVzLCByYW5raW5nKSAlPiUNCiAgZ2F0aGVyKGtleSA9IFR5cGUsIHZhbHVlID0gY291bnQsIC1jKHN0YXRlLCByYW5raW5nLCBjb25maXJtZWQucGVyLnVzKSkgJT4lDQogIA0KICBnZ3Bsb3QoYWVzKGZpbGwgPSBjb3VudCwgeSA9IGNvdW50LCB4ID0gcmVvcmRlcihzdGF0ZSwgLWRlc2MocmFua2luZykpKSkgKyANCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0ID0gImlkZW50aXR5IikgKw0KICBsYWJzKHRpdGxlID0gIjIwIHN0YXRlIGluIFVTIHdpdGggbW9zdCBjb25maXJtZWQgY2FzZXMiKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIiNlYmJjNjIiLCBoaWdoID0gIiNiNDIwMDYiKSArDQogIHhsYWIoIiIpICsgDQogIHlsYWIoIkNvbmZpcm1lZCBDYXNlcyIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1wYXN0ZTAoY29uZmlybWVkLnBlci51cywgIiUiKSksIHNpemU9Mywgdmp1c3Q9LTAuNSkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLHZqdXN0ID0gMC41KSkrDQogIHRoZW1lKA0KICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAjbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiDQogICkNCg0KZy51cy50b3AxDQoNCmdseS51cy50b3AxIDwtIGdncGxvdGx5KGcudXMudG9wMSkNCmdseS51cy50b3AxDQoNCmBgYA0KDQpgYGB7cn0NCmRhdGEudXMubGF0ZXN0LmNvcnIgPC0gZGZfdXMgJT4lZmlsdGVyKGRhdGU9PW1heC5kYXRlLnVzKQ0KVmlldyhkYXRhLnVzLmxhdGVzdC5jb3JyKQ0KDQpkYXRhLnVzLmxhdGVzdC5jb3JyICU8PiUgbXV0YXRlKHJhbmtpbmcgPSBkZW5zZV9yYW5rKGRlc2MoY29uZmlybWVkKSkpICU+JSBhcnJhbmdlKHJhbmtpbmcpDQp0b3AudXMgPC0gZGF0YS51cy5sYXRlc3QuY29yclssMl0NCiN0b3AudXMNCmBgYA0KYGBge3J9DQojIExpc3Qgb2YgdG9wIDIwIHN0YXRlDQprIDwtIDE5DQpkYXRhLnVzLmxhdGVzdC50b3AgPC0gZGF0YS51cy5sYXRlc3QuY29yciAlPiUNCiAgICBmaWx0ZXIocmFua2luZyA8PSBrKzEpICU+JSANCiAgICBhcnJhbmdlKHJhbmtpbmcpIA0KVmlldyhkYXRhLnVzLmxhdGVzdC50b3ApDQp1cy5zdGF0ZS50b3AgPC0gZGF0YS51cy5sYXRlc3QudG9wICU+JSBwdWxsKHN0YXRlKSAlPiUgYXMuY2hhcmFjdGVyKCkNCnVzLnN0YXRlLnRvcCAgJT4lIHNldGRpZmYoJ1VTJykgJT4lIHByaW50KCkNCiNWaWV3KHVzLnN0YXRlLnRvcCkNCmBgYA0KDQoNCg0KYGBge3J9DQojZ2VuZGVyIGluIHVzDQpkZl91c19nZW5kZXIgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gZGF0YV9nZW5kZXIiKSkNCmRmX3VzX2dlbmRlciA8LSBhcy5kYXRhLmZyYW1lKGRmX3VzX2dlbmRlcikNCmRmX3VzX2dlbmRlciA8LSBzZWxlY3QoZGZfdXNfZ2VuZGVyLGMoIlN0YXRlIiwiTWFsZSIsIkZlbWFsZSIpKQ0KZGZfdXNfZ2VuZGVyIDwtIHJlbmFtZShkZl91c19nZW5kZXIsInN0YXRlIj0iU3RhdGUiKQ0KZGZfdXNfZ2VuZGVyDQoNCiNwb3B1bGF0aW9uIGluIHVzDQpkZl91c19wb3AgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gZGF0YV9wb3B1bGF0aW9uIikpDQpkZl91c19wb3AgPC0gYXMuZGF0YS5mcmFtZShkZl91c19wb3ApDQpkZl91c19wb3AgPC0gc2VsZWN0KGRmX3VzX3BvcCxjKCJTdGF0ZSIsIlBvcHVsYXRpb24iKSkNCmRmX3VzX3BvcCA8LSByZW5hbWUoZGZfdXNfcG9wLCJzdGF0ZSI9IlN0YXRlIikNCmRmX3VzX3BvcA0KDQojbG9ja2Rvd24gaW4gdXMNCmRmX3VzX2xvY2tkb3duIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGRhdGFfbG9ja2Rvd24iKSkNCmRmX3VzX2xvY2tkb3duIDwtIGFzLmRhdGEuZnJhbWUoZGZfdXNfbG9ja2Rvd24pDQpkZl91c19sb2NrZG93biA8LSBzZWxlY3QoZGZfdXNfbG9ja2Rvd24sYygiU3RhdGUiLCJEYXkgbG9ja2Rvd24iKSkNCmRmX3VzX2xvY2tkb3duIDwtIHJlbmFtZShkZl91c19sb2NrZG93biwic3RhdGUiPSJTdGF0ZSIpDQpkZl91c19sb2NrZG93bg0KDQojR0RQIGluIHVzDQpkZl91c19nZHAgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gdXNfZ2RwIikpDQpkZl91c19nZHAgIDwtIGFzLmRhdGEuZnJhbWUoZGZfdXNfZ2RwICkNCmRmX3VzX2dkcCAgPC0gc2VsZWN0KGRmX3VzX2dkcCAsYygiU3RhdGUiLCJHRFBzIikpDQpkZl91c19nZHAgIDwtIHJlbmFtZShkZl91c19nZHAgLCJzdGF0ZSI9IlN0YXRlIikNCmRmX3VzX2dkcCANCg0KI2hvbWVsZXNzIGluIHVzDQpkZl91c19ob21lbGVzcyA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSB1c19ob21lbGVzcyIpKQ0KZGZfdXNfaG9tZWxlc3MgPC0gYXMuZGF0YS5mcmFtZShkZl91c19ob21lbGVzcykNCmRmX3VzX2hvbWVsZXNzIDwtIHNlbGVjdChkZl91c19ob21lbGVzcyxjKCJTdGF0ZSIsIkhvbWVsZXNzIikpDQpkZl91c19ob21lbGVzcyA8LSByZW5hbWUoZGZfdXNfaG9tZWxlc3MsInN0YXRlIj0iU3RhdGUiKQ0KZGZfdXNfaG9tZWxlc3MNCg0KI1ZpZXcoZGZfdXMpDQoNCg0KYGBgDQpgYGB7cn0NCiNtZXJnZQ0KbWVyZ2NvdW50cnkgPSBmdW5jdGlvbihkYXRhMSxkYXRhMil7DQogIGRhdGEgPC0gbWVyZ2UoeCA9IGRhdGExLCB5ID0gZGF0YTIsIGJ5ID0gInN0YXRlIiwgYWxsLnggPSBUUlVFKSANCiAgcmV0dXJuKGRhdGEpDQp9DQoNCmRhdGEudG9wLnN0YXRlIDwtIGRhdGEudXMubGF0ZXN0LnRvcCAlPiUgZmlsdGVyKHN0YXRlICE9ICJVUyIpICU+JQ0KICBzZWxlY3Qoc3RhdGUsIGNvbmZpcm1lZCwgZGVhdGhzKQ0KI1ZpZXcoZGF0YS50b3Auc3RhdGUpDQoNCmRmX0FsbHVzIDwtIG1lcmdjb3VudHJ5KGRhdGEudG9wLnN0YXRlLCBkZl91c19nZW5kZXIpDQoNCmRmX0FsbHVzIDwtIG1lcmdjb3VudHJ5KGRmX0FsbHVzLCBkZl91c19wb3ApIA0KDQpkZl9BbGx1cyA8LSBtZXJnY291bnRyeShkZl9BbGx1cywgZGZfdXNfbG9ja2Rvd24pIA0KDQpkZl9BbGx1cyA8LSBtZXJnY291bnRyeShkZl9BbGx1cywgZGZfdXNfZ2RwKQ0KDQpkZl9BbGx1cyA8LSBtZXJnY291bnRyeShkZl9BbGx1cywgZGZfdXNfaG9tZWxlc3MpDQoNCiNWaWV3KGRmX0FsbHVzKQ0KYGBgDQoNCmBgYHtyfQ0KaW5kZXggPC0gaXMubmEoZGZfQWxsdXMpDQpkZl9BbGx1c1tpbmRleF0gPC0gMA0KVmlldyhkZl9BbGx1cykNCg0Kbm9ybWFsaXplID0gZnVuY3Rpb24oZGF0YSl7DQogICNyZXR1cm4gKChkYXRhIC0gbWluKGRhdGEsbmEucm0gPSBUUlVFKSkvKG1heChkYXRhLG5hLnJtID0gVFJVRSkgLSBtaW4oZGF0YSxuYS5ybSA9IFRSVUUpKSkNCiAgeiA8LSBzY2FsZShkYXRhKTsNCiAgdGFuaCh6LzIpDQp9DQpBbGx1cyA9IGFzLmRhdGEuZnJhbWUoYXBwbHkoZGZfQWxsdXNbLDI6OV0sMixub3JtYWxpemUpKQ0KY29ycl9kYXRhVVMgPC0gQWxsdXMgDQoNClZpZXcoY29ycl9kYXRhVVMpDQpgYGANCg0KYGBge3J9DQpBbGx1cyRzdGF0ZSA8LSBjKGRmX0FsbHVzJHN0YXRlKQ0KVmlldyhBbGx1cykNCg0KY29ycl9kYXRhVVMgPC0gcmVuYW1lKGNvcnJfZGF0YVVTICwiRGF5bG9ja2Rvd24iID0gIkRheSBsb2NrZG93biIpDQpgYGANCg0KYGBge3J9DQpBbGx1c19wbG90IDwtIHNlbGVjdChBbGx1cywic3RhdGUiLCJjb25maXJtZWQiLCJkZWF0aHMiLCJNYWxlIiwiRmVtYWxlIiwiUG9wdWxhdGlvbiIsIkRheSBsb2NrZG93biIsIkdEUHMiLCJIb21lbGVzcyIpDQpBbGx1c19wbG90ICU8PiUgZ2F0aGVyKGtleT10eXBlLCB2YWx1ZT1jb3VudCwgLWMoc3RhdGUpKQ0KbGV2ZWxfb3JkZXIgPC0gZmFjdG9yKEFsbHVzX3Bsb3QkdHlwZSwgDQogICAgICAgICAgICAgICAgICAgICAgbGV2ZWwgPSBjKCJjb25maXJtZWQiLCJkZWF0aHMiLCJNYWxlIiwiRmVtYWxlIiwiUG9wdWxhdGlvbiIsIkRheSBsb2NrZG93biIsIkdEUHMiLCJIb21lbGVzcyIpKQ0KZ2dwbG90KGRhdGEgPSBBbGx1c19wbG90LCBhZXMoeD1zdGF0ZSwgeT1sZXZlbF9vcmRlciwgZmlsbD1jb3VudCkpICsgDQogIGdlb21fdGlsZSgpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAicGluayIsIGhpZ2ggPSAiYmx1ZSIpICsNCiAgeGxhYigiIikgKw0KICB5bGFiKCIiKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLHZqdXN0ID0gMSkpKw0KICB0aGVtZSgNCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgI2xlZ2VuZC5wb3NpdGlvbiA9ICJub25lIg0KICApICtsYWJzKHRpdGxlPSdUaGUgaGVhdG1hcCBvZiBDT1ZJRC0xOSBpbmZlY3Rpb25zIGluIFVTJykNCmBgYA0KDQoNCmBgYHtyfQ0KI2NvcnJlbGF0aW9uDQpjb3JyX2RhdGFVUyAlPD4lIHNlbGVjdChjKGNvbmZpcm1lZCxkZWF0aHMsTWFsZSxGZW1hbGUsUG9wdWxhdGlvbixEYXlsb2NrZG93bixHRFBzLEhvbWVsZXNzKSkNCmhlYWQoY29ycl9kYXRhVVMpDQpjb3IoY29ycl9kYXRhVVMpDQpnZ2NvcnJwbG90KGNvcihjb3JyX2RhdGFVUyksaGMub3JkZXIgPSBUUlVFLA0KICAgICAgICAgICBvdXRsaW5lLmNvbG9yID0gIndoaXRlIiwNCiAgICAgICAgICAgY29sb3JzID0gYygiIzZEOUVDMSIsIndoaXRlIiwiI0U0NjcyNiIpLA0KICAgICAgICAgICBsYWIgPSBUUlVFKQ0KYGBgDQoNCmBgYHtyfQ0KI0NvdmlkX1RoYWlsYW5kDQpkZl90aGFpIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGNvdmlkMTlfVGhhaWxhbmQiKSkNCmRmX3RoYWkgPC0gYXMuZGF0YS5mcmFtZShkZl90aGFpKQ0KVmlldyhkZl90aGFpKQ0KYGBgDQoNCmBgYHtyfQ0KI2NsZWFuIENvdmlkX1RoYWlsYW5kDQpkYXRlcy50aCA8LSBkZl90aGFpWywyXSU+JSBtZHkoKQ0KcmFuZ2UoZGF0ZXMudGgpDQptaW4uZGF0ZS50aCA8LSBtaW4oZGF0ZXMudGgpDQptYXguZGF0ZS50aCA8LSBtYXgoZGF0ZXMudGgpDQptaW4uZGF0ZS50eHQudGggPC0gbWluLmRhdGUudGggJT4lIGZvcm1hdCgnJWQgJWIgJVknKQ0KbWF4LmRhdGUudHh0LnRoIDwtIG1heC5kYXRlLnRoICU+JSBmb3JtYXQoJyVkICViICVZJykNCmBgYA0KDQpgYGB7cn0NCmRmX3RoYWkkYW5ub3VuY2VfZGF0ZSA8LSBtZHkoZGZfdGhhaSRhbm5vdW5jZV9kYXRlKQ0KZGZfdGhhaSRub3RpZmljYXRpb25fZGF0ZSA8LSBtZHkoZGZfdGhhaSRub3RpZmljYXRpb25fZGF0ZSkNCmRmX3RoYWkNCmBgYA0KDQpgYGB7cn0NCmRmX3RoYWkgPC0gZGZfdGhhaSAlPiUgc2VsZWN0KCFOby4pICU+JSBzZWxlY3QoIW5vdGlmaWNhdGlvbl9kYXRlKSAlPiUgDQogIGdyb3VwX2J5KGFubm91bmNlX2RhdGUpDQpkZl90aGFpDQpgYGANCg0KYGBge3J9DQojIFRvdGFsIGNvbmZpcm1lZCBjYXNlcyBpbiBUaGFpbGFuZA0KZGF0YS50aGFpLmNvdW50IDwtIGRmX3RoYWkgJT4lDQogIHNlbGVjdChhbm5vdW5jZV9kYXRlKSAlPiUNCiAgc3VtbWFyaXNlKGNvbWZpcm1lZCA9IG4oKSkgJT4lIGFzLmRhdGEuZnJhbWUoKQ0KVmlldyhkYXRhLnRoYWkuY291bnQpDQpkYXRhLnRoYWkuY291bnQkY3VtdWxhdGl2ZV9jb25maXJtZWQgPC0gY3Vtc3VtKGRhdGEudGhhaS5jb3VudFssIDJdKQ0KVmlldyhkYXRhLnRoYWkuY291bnQpDQpgYGANCg0KYGBge3J9DQojIyBUaGFpIENvbmZpcm1lZCBDYXNlcyAoSmFuIDIwMjAgLSBKYW4gMjAyMQ0KcGxvdDEgPC0gZ2dwbG90KGRhdGEudGhhaS5jb3VudCwgYWVzKHg9YW5ub3VuY2VfZGF0ZSwgeT1jdW11bGF0aXZlX2NvbmZpcm1lZCkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoIiAiKSArIHlsYWIoIkNvdW50IikgKyBsYWJzKHRpdGxlPSdUaGFpIEN1bXVsYXRpdmUgQ29uZmlybWVkIENhc2VzIChKYW4gMjAyMCAtIEphbiAyMDIxKScpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCnBsb3QyIDwtIGdncGxvdChkYXRhLnRoYWkuY291bnQsIGFlcyh4PWFubm91bmNlX2RhdGUsIHk9Y29tZmlybWVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgICB4bGFiKCIgIikgKyB5bGFiKCJDb3VudCIpKyBsYWJzKHRpdGxlPSdUaGFpIENvbmZpcm1lZCBDYXNlcyAoSmFuIDIwMjAgLSBKYW4gMjAyMSBsb2cgc2NhbGUpJykgKw0KICAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkrc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPSdsb2cxMCcpDQojIyBzaG93IHR3byBwbG90cyBzaWRlIGJ5IHNpZGUNCmdyaWQuYXJyYW5nZShwbG90MSwgcGxvdDIsIG5jb2w9MSkNCmBgYA0KDQpgYGB7cn0NCiMgQ29uZmlybWVkIGNhc2VzIGRpdmlkZWQgYnkgc2V4IChnZW5kZXIpDQpkZl90aGFpJHNleFtkZl90aGFpJHNleCA9PSAiIl0gPC0gIlVua25vd24iDQpkYXRhLnRoYWkuZ2VuZGVyIDwtIGRmX3RoYWkgJT4lDQogIGdyb3VwX2J5KHNleCkgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lDQogIG11dGF0ZShwZXJjZW50ID0gKGNvdW50IC8gc3VtKGNvdW50KSAqIDEwMCkgJT4lIHJvdW5kKDIpKSAlPiUNCiAgZmlsdGVyKHBlcmNlbnQ+MSklPiUNCiAgI211dGF0ZShwb3MgPSBjdW1zdW0ocGVyY2VudCkgLSAwLjUqcGVyY2VudCkgJT4lDQogIGFycmFuZ2UoZGVzYyhwZXJjZW50KSkNCg0KZGF0YS50aGFpLmdlbmRlcg0KYGBgDQoNCmBgYHtyfQ0KZGF0YS50aGFpLmdlbmRlciRzZXggPC0gZmFjdG9yKGRhdGEudGhhaS5nZW5kZXIkc2V4LCBsZXZlbHMgPSBhcy5jaGFyYWN0ZXIoZGF0YS50aGFpLmdlbmRlciRzZXgpKQ0KZGF0YS50aGFpLmdlbmRlciRzZXgNCmcudGguZ2VuZGVyIDwtIGRhdGEudGhhaS5nZW5kZXIgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSAiIiwgeSA9IHBlcmNlbnQsIGZpbGwgPSBzZXgpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDEpICsNCiAgY29vcmRfcG9sYXIoInkiKSArDQogIHRoZW1lX3ZvaWQoKSArDQogIGxhYnModGl0bGU9J0dlbmRlciBvZiBUaGFpIENvbmZpcm1lZCBDYXNlcyAoSmFuIDIwMjAgLSBKYW4gMjAyMSknKSsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChwZXJjZW50LCAiJSIpKSwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gNSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpICsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUpKSANCmcudGguZ2VuZGVyDQpgYGANCg0KYGBge3J9DQojIENvbmZpcm1lZCBjYXNlcyBkaXZpZGVkIGJ5IHByb3ZpbmNlX29mX29uc2V0DQpkYXRhLnRoYWkub25zZXQgPC0gZGZfdGhhaSAlPiUNCiAgICBncm91cF9ieShwcm92aW5jZV9vZl9vbnNldCkgJT4lDQogICAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUNCiAgICBhcnJhbmdlKGRlc2MoY291bnQpKSU+JQ0KICAgIHJlbmFtZShvbnNldD0iY291bnQiKSU+JQ0KICAgIHJlbmFtZShwcm92aW5jZT0icHJvdmluY2Vfb2Zfb25zZXQiKQ0KZGF0YS50aGFpLm9uc2V0JHByb3ZpbmNlW2RhdGEudGhhaS5vbnNldCRwcm92aW5jZSA9PSAiIl0gPC0gIlVua25vd24iDQpkYXRhLnRoYWkub25zZXQNCmBgYA0KDQpgYGB7cn0NCiMgQ29uZmlybWVkIGNhc2VzIGRpdmlkZWQgYnkgcHJvdmluY2Vfb2ZfaXNvbGF0aW9uIA0KZGF0YS50aGFpLmlzb2xhdGlvbiA8LSBkZl90aGFpICU+JQ0KICAgIGdyb3VwX2J5KHByb3ZpbmNlX29mX2lzb2xhdGlvbikgJT4lDQogICAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUNCiAgICBhcnJhbmdlKGRlc2MoY291bnQpKSU+JQ0KICAgIHJlbmFtZShpc29sYXRpb24gPSJjb3VudCIpJT4lDQogICAgcmVuYW1lKHByb3ZpbmNlPSJwcm92aW5jZV9vZl9pc29sYXRpb24iKQ0KDQpkYXRhLnRoYWkuaXNvbGF0aW9uJHByb3ZpbmNlW2RhdGEudGhhaS5pc29sYXRpb24kcHJvdmluY2UgPT0gIiJdIDwtICJVbmtub3duIg0KZGF0YS50aGFpLmlzb2xhdGlvbg0KYGBgDQoNCmBgYHtyfQ0KZGF0YS5wcm92aW5jZSA8LSBtZXJnZSh4ID0gZGF0YS50aGFpLm9uc2V0LCB5ID0gZGF0YS50aGFpLmlzb2xhdGlvbiwgYnkgPSAicHJvdmluY2UiLCBhbGwueCA9IFRSVUUpIA0KZGF0YS5wcm92aW5jZSA8LSBkYXRhLnByb3ZpbmNlJT4lZmlsdGVyKG9uc2V0PjEyNSkNCmRhdGEucHJvdmluY2UNCmBgYA0KDQpgYGB7cn0NCmRhdGEucHJvdmluY2UubG9uZyA8LSBkYXRhLnByb3ZpbmNlICU+JQ0KICAgIGdhdGhlcihrZXk9dHlwZSwgdmFsdWU9Y291bnQsIC1jKHByb3ZpbmNlKSkNCmRhdGEucHJvdmluY2UubG9uZw0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEucHJvdmluY2UubG9uZyxhZXMoeD1wcm92aW5jZSx5PWNvdW50KSkrDQogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIscG9zaXRpb24gPSAiZG9kZ2UiLGFlcyhmaWxsPXR5cGUpKSsNCiAgICBsYWJzKHRpdGxlID0gIlByb3ZpbmNlIG9mIG9uc2V0IGFuZCBQcm92aW5jZSBvZiBpc29sYXRpb24gaW4gVGhhaWxhbmQiKSsNCiAgICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgI2xlZ2VuZC5wb3NpdGlvbj0nbm9uZScsDQogICAgICAgICAgICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEyKSwNCiAgICAgICAgICAgICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMCksDQogICAgICAgICAgICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSsgeGxhYignJykgKyB5bGFiKCdDb3VudCcpDQpgYGANCg0KYGBge3J9DQojIENvbmZpcm1lZCBjYXNlcyBkaXZpZGVkIGJ5IHJpc2sNCmRhdGEudGhhaS5yaXNrIDwtIGRmX3RoYWkgJT4lDQogIGdyb3VwX2J5KHJpc2spICU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQ0KICBhcnJhbmdlKGRlc2MoY291bnQpKQ0KZGF0YS50aGFpLnJpc2sNCmBgYA0KDQpgYGB7cn0NCiMgQ29uZmlybWVkIGNhc2VzIGRpdmlkZWQgYnkgcmlzaw0KZGF0YS50aGFpLnJpc2sgPC0gZGZfdGhhaSAlPiUNCiAgZ3JvdXBfYnkocmlzaykgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lDQogIG11dGF0ZShwZXJjZW50ID0gKGNvdW50IC8gc3VtKGNvdW50KSAqIDEwMCkgJT4lIHJvdW5kKDIpKSAlPiUNCiAgZmlsdGVyKHBlcmNlbnQ+MC43NikgJT4lDQogIGFycmFuZ2UoZGVzYyhwZXJjZW50KSkgDQpkYXRhLnRoYWkucmlzaw0KYGBgDQoNCmBgYHtyfQ0KZGF0YS50aGFpLnJpc2skcmlza1tkYXRhLnRoYWkucmlzayRyaXNrID09ICJDIl0gPC0gIkNsb3NlIGNvbnRhY3Qgd2l0aCB0aGUgcGF0aWVudCINCmRhdGEudGhhaS5yaXNrJHJpc2tbZGF0YS50aGFpLnJpc2skcmlzayA9PSAiQ0d1c3REciBTYW11dCBTYUdHQUYiXSA8LSAiQ2x1c3RlciBTYW11dCBTYWtob24iDQpkYXRhLnRoYWkucmlzayRyaXNrW2RhdGEudGhhaS5yaXNrJHJpc2sgPT0gIkYiXSA8LSAiU3RhdGUgUXVhcmFudGluZSINCmRhdGEudGhhaS5yaXNrJHJpc2tbZGF0YS50aGFpLnJpc2skcmlzayA9PSAiRyJdIDwtICJHbyB0byBhIGNyb3dkZWQgcGxhY2UiDQpkYXRhLnRoYWkucmlzayRyaXNrW2RhdGEudGhhaS5yaXNrJHJpc2sgPT0gIkEiXSA8LSAiUGVvcGxlIHRyYXZlbGxpbmcgZnJvbSBhYnJvYWQiDQpkYXRhLnRoYWkucmlzayRyaXNrW2RhdGEudGhhaS5yaXNrJHJpc2sgPT0gIlVGR0ZBd0YiXSA8LSAiVW5rbm93biINCmRhdGEudGhhaS5yaXNrJHJpc2tbZGF0YS50aGFpLnJpc2skcmlzayA9PSAiRCJdIDwtICJDYXJlZXIgYXQgcmlzayINCmRhdGEudGhhaS5yaXNrJHJpc2tbZGF0YS50aGFpLnJpc2skcmlzayA9PSAiQ0d1c3REciBSYXlBRmciXSA8LSAiQ2x1c3RlciBSYXlvbmciDQpkYXRhLnRoYWkucmlzayRyaXNrW2RhdGEudGhhaS5yaXNrJHJpc2sgPT0gIkNHdXN0RHIgUGF0dGF5YSBDYXNDRkEiXSA8LSAiQ2x1c3RlciBQYXR0YXlhIENhc2lubyINCmRhdGEudGhhaS5yaXNrJHJpc2tbZGF0YS50aGFpLnJpc2skcmlzayA9PSAiQ0d1c3REciBBRmcgVEdBRmciXSA8LSAiQ2x1c3RlciBBbmcgVGhvbmciDQpkYXRhLnRoYWkucmlzaw0KYGBgDQoNCmBgYHtyfQ0KZGF0YS50aGFpLnJpc2skcmlzayA8LSBmYWN0b3IoZGF0YS50aGFpLnJpc2skcmlzaywgbGV2ZWxzID0gYXMuY2hhcmFjdGVyKGRhdGEudGhhaS5yaXNrJHJpc2spKQ0KZGF0YS50aGFpLnJpc2skcmlzaw0KZy50aC5yaXNrIDwtIGRhdGEudGhhaS5yaXNrICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gIiIsIHkgPSBwZXJjZW50LCBmaWxsID0gcmlzaykpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMC41KSArDQogIGNvb3JkX3BvbGFyKCJ5IikgKw0KICB0aGVtZV92b2lkKCkgKw0KICBsYWJzKHRpdGxlPSdSaXNrIG9mIFRoYWkgQ29uZmlybWVkIENhc2VzKEphbiAyMDIwIC0gSmFuIDIwMjEpJykrDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZTAocGVyY2VudCwgIiUiKSksIGNvbG9yID0gIkJsYWNrIiwgc2l6ZSA9IDMsIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpKSArDQogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHJldmVyc2UgPSBUUlVFKSkgDQpnLnRoLnJpc2sNCmBgYA0KDQpgYGB7cn0NCiMgQ29uZmlybWVkIGNhc2VzIGRpdmlkZWQgYnkgYWdlDQpkYXRhLnRoYWkuYWdlIDwtIGRmX3RoYWkgJT4lDQogIGdyb3VwX2J5KGFnZSxzZXgpICU+JSANCiAgZmlsdGVyKGFnZSAhPSAwKSU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQ0KICBhcnJhbmdlKGRlc2MoY291bnQpKQ0KZGF0YS50aGFpLmFnZQ0KYGBgDQoNCmBgYHtyfQ0KZGF0YS50aGFpLmFnZSA8LSBkYXRhLnRoYWkuYWdlIyAlPiUgZmlsdGVyKHNleCAhPSAiVW5rbm93biIpDQpnZ3Bsb3QoZGF0YS50aGFpLmFnZSxhZXMoeD1hZ2UseT1jb3VudCxmaWxsPXNleCkpK2dlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSsNCiAgICAgIGxhYnModGl0bGU9J0FnZSBvZiBUaGFpIENvbmZpcm1lZCBDYXNlcyAoU3RhY2spJykrZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHJldmVyc2UgPSBUKSkNCmBgYA0KDQpgYGB7cn0NCiMgQ29uZmlybWVkIGNhc2VzIGRpdmlkZWQgYnkgbmF0aW9uYWxpdHkNCmRhdGEudGhhaS5uYXRpb25hbGl0eSA8LSBkZl90aGFpICU+JQ0KICBncm91cF9ieShuYXRpb25hbGl0eSkgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lDQogIGZpbHRlcihjb3VudCA+IDExKSU+JQ0KICBhcnJhbmdlKGRlc2MoY291bnQpKQ0KZGF0YS50aGFpLm5hdGlvbmFsaXR5JG5hdGlvbmFsaXR5W2RhdGEudGhhaS5uYXRpb25hbGl0eSRuYXRpb25hbGl0eSA9PSAiPz8/Pz8/Pz8iXSA8LSAiVW5rbm93biINCmRhdGEudGhhaS5uYXRpb25hbGl0eSRuYXRpb25hbGl0eVtkYXRhLnRoYWkubmF0aW9uYWxpdHkkbmF0aW9uYWxpdHkgPT0gIiJdIDwtICJVbmtub3duIg0KZGF0YS50aGFpLm5hdGlvbmFsaXR5JG5hdGlvbmFsaXR5W2RhdGEudGhhaS5uYXRpb25hbGl0eSRuYXRpb25hbGl0eSA9PSAiQnVybWEiXSA8LSAiTXlhbm1hciINCmRhdGEudGhhaS5uYXRpb25hbGl0eQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEudGhhaS5uYXRpb25hbGl0eSkrIA0KICBnZW9tX2JhcihhZXMoeD1uYXRpb25hbGl0eSx5PWNvdW50LGZpbGwgPSBjb3VudCksc3RhdCA9ICJpZGVudGl0eSIsc2hvdy5sZWdlbmQgPSBGKSsNCiAgICBnZ3RpdGxlKCdOYXRpb25hbGl0eSBvZiBUaGFpIENvbmZpcm1lZCBDYXNlcyAobG9nIHNjYWxlKScpKw0KICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSsNCiAgICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwNCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwNCiAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkrDQogICAgeGxhYigiIikreWxhYigiTnVtYmVyIG9mIE5hdGlvbmFsaXR5IikrDQogICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9J2xvZzEwJykrY29vcmRfZmxpcCgpK3NjYWxlX2ZpbGxfZ3JhZGllbnQobG93PSJibHVlIixoaWdoID0gInJlZCIpDQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==